/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * 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 Business Objects 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. */ /* * VariablesDisplay.java * Creation date: (09/07/01 1:37:20 PM) * By: Edward Lam */ package org.openquark.gems.client; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.FocusListener; import java.awt.event.MouseEvent; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.swing.BorderFactory; import javax.swing.DefaultListModel; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.ListCellRenderer; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import javax.swing.border.EmptyBorder; import org.openquark.gems.client.caleditor.AdvancedCALEditor; import org.openquark.gems.client.utilities.MouseClickDragAdapter; /** * The JPanel displaying the free variables within a code gem editor * @author Iulian Radu */ public class VariablesDisplay extends JPanel { private static final long serialVersionUID = -959269253038046741L; /** * Interface for a listener which wishes to be informed of various panel events * Creation date: (Jul 15, 2002 3:20:12 PM) * @author Edward Lam */ public interface PanelEventListener { /** * Notify the listener that a panel shifted to another position. * @param argIndex the index of the shifted panel. * @param shiftAmount the amount by which the panel was shifted. * +ve numbers increase its index, -ve numbers decrease it. */ public void panelShifted(int argIndex, int shiftAmount); /** * Notify the listener that a panel's type icon was double-clicked. * @param variablePanel the variable panel that was double-clicked. */ public void panelTypeIconDoubleClicked(VariablePanel variablePanel); } /** * The JList Component that displays the variables. * Creation date: (09/07/01 1:37:20 PM) * @author Edward Lam */ class VariablesDisplayList extends JList { private static final long serialVersionUID = -3427641675953452151L; /** Reference to a MouseHandler */ private final MouseHandler mouseHandler; /** * A trivial implementation of ListCellRenderer. * Creation date: (04/07/2001 3:57:15 PM) * @author Edward Lam */ public class VariablesDisplayRenderer implements ListCellRenderer { /** * Return an object that can paint a VariablePanel (ie. itself). * @param list Jlist - The JList we're painting. * @param value Object - The value returned by list.getModel().getElementAt(index). * @param index int - The cell's index. * @param isSelected boolean - True if the specified cell was selected. * @param cellHasFocus boolean - True if the specified cell has the focus. * @return Component the component capable of rendering the list item */ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { // the list objects should all be VariablePanel objects VariablePanel varPan = (VariablePanel)value; varPan.setSelected(isSelected); // gray out the variable panel if it's disabled varPan.setGrayed(!varPan.isEnabled()); return varPan; } } /** * Inner class to handle mouse and drag&drop events on the Variables Display. * * Reordering of panels within this display is handled by this class, while * item transfer between the display and external components is directed to * native drag & drop, making use of transfer handlers. * */ private class MouseHandler extends MouseClickDragAdapter implements DropTargetListener { /** The index of the variable panel clicked, if any. */ private int panelNumClicked; /** The position of the separator between panels. */ private int separatorPosition = -1; /** * Constructor for the Mouse Handler */ private MouseHandler() { } /** * Move the drag mode into the aborted state. */ @Override protected void abortDrag() { super.abortDrag(); clearSelection(); panelClicked = null; exitDragState(null); getDropTarget().getDropTargetContext().dropComplete(false); } /** * Invoked when a mouse button has been pressed on a component. */ @Override public void mousePressed(MouseEvent e){ super.mousePressed(e); panelClicked = getClickedPanel(e); // clear the selection if we clicked on a blank area if (panelClicked == null) { clearSelection(); } maybeShowPopup(e); } /** * If mouse event triggers popup, show it * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent) */ @Override public void mouseReleased(MouseEvent e) { super.mouseReleased(e); maybeShowPopup(e); } /** * Surrogate method for mouseClicked. Called only when our definition of click occurs. * @param e MouseEvent the relevant event * @return boolean true if the click was a double click */ @Override public boolean mouseReallyClicked(MouseEvent e){ boolean doubleClicked = super.mouseReallyClicked(e); panelClicked = getClickedPanel(e); // clear the selection if we clicked on a blank area if (panelClicked == null) { clearSelection(); // otherwise, if we double clicked a panel's type icon, notify the listener } else if (doubleClicked && checkTypeIconHit(e)) { notifyPanelTypeIconDoubleClicked(panelClicked); } return doubleClicked; } /** * Carry out setup appropriate to enter the drag state. * Principal effect is to change dragMode as appropriate. * @param e MouseEvent the mouse event which triggered entry into the drag state. */ @Override public void enterDragState(MouseEvent e) { super.enterDragState(e); // Nothing hit - deselect everything clearSelection(); // ignore anything that is not a left mouse button if (!SwingUtilities.isLeftMouseButton(e)) { return; } // Get the part which the user clicked panelClicked = getClickedPanel(e); // Did they hit anything? if (panelClicked != null) { // gray out the clicked panel and, give drag control to drag&drop panelClicked.setEnabled(false); exitDragState(e); getTransferHandler().exportAsDrag(VariablesDisplayList.this, e, TransferHandler.MOVE); } else { abortDrag(); return; } } /** * Get the Variable panel which was clicked. * @param e MouseEvent the related mouse event * @return VariablePanel the VariablePanel which was clicked */ private VariablePanel getClickedPanel(MouseEvent e) { // check that there is a panel clicked Point clickLocation = e.getPoint(); panelNumClicked = locationToIndex(clickLocation); if (-1 < panelNumClicked && getCellBounds(panelNumClicked, panelNumClicked).contains(clickLocation)) { return (VariablePanel)getModel().getElementAt(panelNumClicked); } else { return null; } } /** * Find out whether a mouse event's location corresponds to a panel's type icon * @param e MouseEvent the relevant event * @return boolean whether a panel's type icon was hit. */ private boolean checkTypeIconHit(MouseEvent e) { VariablePanel varPan = getClickedPanel(e); if (varPan == null) { return false; } // We have to translate the point from the JList's coordinate system to the label's // Unfortunately, the JList doesn't actually contain labels (since it uses a renderer instead) // so we have to do this manually. Point clickPoint = e.getPoint(); int panelIndex = locationToIndex(clickPoint); Rectangle panelBounds = getCellBounds(panelIndex, panelIndex); Point convertedPoint = new Point(clickPoint.x - panelBounds.x, clickPoint.y - panelBounds.y); return varPan.checkIconHit(convertedPoint); } /** * Convert a location in the JList to the index of the separator location to which it corresponds. * @param p Point the location * @return int the index of the separator location to which the point corresponds. -1 if it doesn't correspond * to a sensible location. */ private int locationToSeparatorIndex(Point p) { // first check to see if the point is outside the variables display Rectangle visibleRect = getVisibleRect(); if (!visibleRect.contains(p)) { return -1; } // index is the number of panel midpoints above p int index = 0; int numVarPanels = getModel().getSize(); while (index < numVarPanels) { Rectangle rect = getCellBounds(index, index); // compare with the Y coordinate halfway down varPan if (p.getY() < (rect.getY() + rect.getHeight() / 2)) { break; } index++; } // can only place the separator among other arguments int maxIndex = numArgVarPanels; if (index > maxIndex) { index = maxIndex; } return index; } /** * Draw or undraw a separator between elements in the JList * @param index int the index at which to draw the separator * @param undraw boolean true to undraw, false to draw * @return int the index at whcih the separator was really drawn */ private int drawSeparator(int index, boolean undraw) { if (index < 0) { // don't draw return -1; } // Get a graphics object Graphics2D g2d = (Graphics2D)getGraphics(); // Find the appropriate color and enter paint mode if (undraw) { g2d.setColor(getBackground()); } else { g2d.setColor(Color.black); } g2d.setPaintMode(); // preliminary setup int numVarPanels = getModel().getSize(); if (numVarPanels < index) { index = numVarPanels; } // declare a rectangle for the main part of the separator Rectangle rect; if (index < numVarPanels) { rect = getCellBounds(index, index); } else { // below the last element in the list - move to the bottom of the cell rect = getCellBounds(index - 1, index - 1); rect.y += rect.height; } // adjust the height and y-coordinate of the separator rect.height = 1; rect.y -= 1; rect.x -=1; // an additional bleb at the front Polygon poly = new Polygon(); poly.addPoint(rect.x, rect.y-4); poly.addPoint(rect.x, rect.y+rect.height+4); poly.addPoint(rect.x+4, rect.y+(rect.height/2)); // Draw the separator g2d.draw(rect); g2d.fill(poly); // Free the graphics object g2d.dispose(); return index; } /** * Update the displayed position of the separator. * @param index int the new position of the separator */ void updateSeparator(int index) { // preliminary setup int numVarPanels = getModel().getSize(); if (numVarPanels < index) { index = numVarPanels; } // check if anything to do if (separatorPosition == index) { return; } // Turn off separator at last position if (separatorPosition > -1) { drawSeparator(separatorPosition, true); } // Turn on separator at this position drawSeparator(index, false); // update separator position separatorPosition = index; } /** * Notify the listener that a panel shifted. * @param argIndex the index of the shifted panel . * @param shiftAmount the amount by which the panel was shifted. * +ve numbers increase its index, -ve numbers decrease it. */ private void notifyPanelShifted(int argIndex, int shiftAmount) { for (int i = 0; i < panelEventListeners.size(); i++) { panelEventListeners.get(i).panelShifted(argIndex, shiftAmount); } } /** * Notify the listener that a panel's type icon was double-clicked. * @param variablePanel the variable panel whose type icon was double-clicked. */ private void notifyPanelTypeIconDoubleClicked(VariablePanel variablePanel) { for (int i = 0; i < panelEventListeners.size(); i++) { panelEventListeners.get(i).panelTypeIconDoubleClicked(variablePanel); } } /** * Show the popup, if the given mouse event is the popup trigger. * @param e the mouse event. */ private void maybeShowPopup(MouseEvent e) { if (e.isPopupTrigger() && popupProvider != null) { // Get the popup menu for the current identifier if (panelClicked == null) { return; } JPopupMenu menu = popupProvider.getPopupMenu(panelClicked.getIdentifier()); if (menu == null) { return; } menu.show(e.getComponent(), e.getX(), e.getY()); } } /** * Paint the separator. */ public void paintSeparator() { SwingUtilities.invokeLater(new Thread() { @Override public void run() { // It's possible that the user stopped dragging before this Thread was run. // In which case, we don't need to re-draw the separator. if (isUsefulDragMode(dragMode)) { drawSeparator(separatorPosition, false); } } }); } /** * Invoked by drag&drop when a drop operation occurs. * If the transferable item dragged is not produced by this display, * then the external TransferHandler deals with it. Otherwise, a panel * shift operation is performed. * * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent) */ public void drop(DropTargetDropEvent dtde){ // Undraw the separator, and restore cursor regardless of what will happen updateSeparator(-1); setCursor(null); // See if the transferable data was created by this variable display Transferable t = dtde.getTransferable(); try { if (((CodeGemEditor.VariablePanelTransferable.VariableStructure) t.getTransferData(CodeGemEditor.VariablePanelTransferable.variableStructureFlavor)).getParentDisplayList() != VariablesDisplayList.this) { // Transferable was generated by a Variable Display other than ours; // do not accept this. dtde.rejectDrop(); return; } } catch (UnsupportedFlavorException e) { // Transferable does not have variable structure, so it must be external defaultDrop(dtde); return; } catch (IOException e) { // Data no longer available in requested flavor; let the transfer handler deal with this defaultDrop(dtde); return; } // Transferable item was generated by this Variable Display // So we will internally handle variable reordering. // Where are we now? int index = locationToSeparatorIndex(dtde.getLocation()); // If we've finished dragging. Finish up and do the appropriate moves // PanelClicked is a valid panel only if drag operation was started inside // this Variables Display. if (panelClicked != null) { // re-enable the panel panelClicked.setEnabled(true); // A panel may be dragged even if reorderingAllowed is false, since // it may be moved to qualifications display. Now we reorder only // if allowed. if (!reorderingAllowed) { clearSelection(); panelClicked = null; repaint(); return; } // if it's not a valid place to drop -> just go back to the old order if (index < 0) { index = panelNumClicked; } // Now do all the reorder logic to the inputs. Do nothing if nothing changed. List<Object> listData = getListData(); if (index != panelNumClicked && index != panelNumClicked + 1) { int shiftAmount; listData.remove(panelNumClicked); if (index > panelNumClicked) { listData.add(index - 1, panelClicked); shiftAmount = index - panelNumClicked - 1; } else { listData.add(index, panelClicked); shiftAmount = index - panelNumClicked; } setListData(listData.toArray()); notifyPanelShifted(panelNumClicked, shiftAmount); } } clearSelection(); panelClicked = null; repaint(); } /** * Invoked by drag&drop when the user drags an item outside of the display component. * This method clears the display of any drag indicators. * @see java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent) */ public void dragExit(DropTargetEvent dte){ // We are either dragging out of the window, or a drag operation has finished // Cleanup panel updateSeparator(-1); setCursor(null); clearSelection(); repaint(); } /** * @see java.awt.dnd.DropTargetListener#dropActionChanged(java.awt.dnd.DropTargetDragEvent) */ public void dropActionChanged(DropTargetDragEvent dtde){ } /** * Invoked by drag&drop when the user drags an item over the display component. * If the user is dragging an argument, this method displays the list separator * indicating a new spot for the argument. * @see java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent) */ public void dragOver(DropTargetDragEvent dtde){ // If item transfered is not acceptable, reject it // We are dragging an external item; accept it if it can be imported via transfer handler if (externalDragFlavor) { if (!getTransferHandler().canImport(VariablesDisplayList.this, dtde.getCurrentDataFlavors())) { dtde.rejectDrag(); } return; } // We are dragging one of our panels (allowed since it may be moved to qualifications display) // but we cannot drop it because reordering is not allowed if ((panelClicked != null) && (! reorderingAllowed )) { dtde.rejectDrag(); return; } // Item is acceptable; prepare the display // looks nicer if we clear the selection clearSelection(); // Update the on-screen separator int newIndex = locationToSeparatorIndex(dtde.getLocation()); updateSeparator(newIndex); // set the cursor to indicate droppability if (newIndex > -1){ //setCursor(DragSource.DefaultMoveDrop); } else { //setCursor(DragSource.DefaultMoveNoDrop); dtde.rejectDrag(); } } /** * Invoked by drag&drop when the user drags an item into the display component. * This method accepts drags of items created by Variable Displays, and lets the * external transfer handler deal with foreign item types. * @see java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent) */ public void dragEnter(DropTargetDragEvent dtde){ // If dragged item transferable is not something a Variable Display produced, if (!dtde.getCurrentDataFlavorsAsList().contains( CodeGemEditor.VariablePanelTransferable.variableStructureFlavor)) { // Deal with it via the panel transfer handler externalDragFlavor = true; defaultDragEnter(dtde); return; } // We might have produced the transferable being dragged, // so we will handle it internally. // Note: We might not have produced this transferable, even if it is our flavour; // We can only check the owner from transferable contents in the Drop operation, // so we will allow the drag to go on until then. externalDragFlavor = false; dtde.acceptDrag(dtde.getDropAction()); } /** * This is used by default drag&drop methods listed below. * It holds true if the item dragged can be imported; false if not */ private boolean canImport = false; /** * Indicates whether the item dragged has an internal flavour (ie: has been * created by a Variable Display). */ private boolean externalDragFlavor = true; /** * Default handler for dragEnter(); delegates control through external transfer handler. * @see javax.swing.TransferHandler */ public void defaultDragEnter(DropTargetDragEvent e) { DataFlavor[] flavors = e.getCurrentDataFlavors(); JComponent c = (JComponent)e.getDropTargetContext().getComponent(); TransferHandler importer = c.getTransferHandler(); if (importer != null && importer.canImport(c, flavors)) { canImport = true; } else { canImport = false; } int dropAction = e.getDropAction(); if (canImport) { e.acceptDrag(dropAction); } else { e.rejectDrag(); } } /** * Default handler for dragOver(); delegates control through external transfer handler. * @see javax.swing.TransferHandler */ public void defaultDragOver(DropTargetDragEvent e) { int dropAction = e.getDropAction(); if (canImport) { e.acceptDrag(dropAction); } else { e.rejectDrag(); } } /** * Default handler for drop(); delegates control through external transfer handler. * @see javax.swing.TransferHandler */ public void defaultDrop(DropTargetDropEvent e) { int dropAction = e.getDropAction(); JComponent c = (JComponent)e.getDropTargetContext().getComponent(); TransferHandler importer = c.getTransferHandler(); if (canImport && importer != null) { e.acceptDrop(dropAction); try { Transferable t = e.getTransferable(); e.dropComplete(importer.importData(c, t)); } catch (RuntimeException re) { e.dropComplete(false); } } else { e.rejectDrop(); } } /** * Default handler for dragActionChanged(); delegates control through external transfer handler. * @see javax.swing.TransferHandler */ public void defaultDropActionChanged(DropTargetDragEvent e) { int dropAction = e.getDropAction(); if (canImport) { e.acceptDrag(dropAction); } else { e.rejectDrag(); } } } /** * Default constructor for a VariablesDisplay. */ VariablesDisplayList() { super(); setName("VariablesDisplay"); setModel(new DefaultListModel()); setAlignmentY(Component.TOP_ALIGNMENT); setCellRenderer(new VariablesDisplayRenderer()); setBackground(new Color(239,239,239)); setPreferredSize(new Dimension(25, 101)); setAlignmentX(Component.LEFT_ALIGNMENT); setSize(25, 191); setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // add the mouse listener mouseHandler = new MouseHandler(); addMouseListener(mouseHandler); addMouseMotionListener(mouseHandler); setDropTarget(new DropTarget(this, mouseHandler)); } /** * Cleans up variables display after a dragged * item was dropped. This should be called by the * external drag&drop TransferHandler after an item * was exported from this display. */ void externalDropComplete() { if (panelClicked != null) { // Display still believes it is dragging; this means // the drop operation has happened on an external component panelClicked.setEnabled(true); panelClicked = null; } setCursor(null); clearSelection(); repaint(); } /** Retrieves the panel currently handled by the mouse */ VariablePanel getClickedPanel() { return panelClicked; } /** * Paint the Variables Display area. * @param g Graphics the graphics object to use. */ @Override public void paintComponent(Graphics g) { // JList super.paintComponent(g); // paint the separator mouseHandler.paintSeparator(); } } /** List contained within this display */ private final VariablesDisplayList variablesDisplayList = new VariablesDisplayList(); /** Reference to listeners on argument reorder events. */ private final List<PanelEventListener> panelEventListeners = new ArrayList<PanelEventListener>(); /** Whether reordering of the variables is allowed (eg. not allowed if it's broken..). */ private boolean reorderingAllowed = false; /** The number of variable panels which represent arguments. Needed for arg reordering. */ private int numArgVarPanels = 0; /** The VariablePanel which is handled by the mouse (null if none). */ private VariablePanel panelClicked; /** Label displaying title */ private final JLabel titleLabel = new JLabel(); /** Provider for popup menus for this display */ private AdvancedCALEditor.IdentifierPopupMenuProvider popupProvider; /** Constructor for the display */ public VariablesDisplay() { setLayout(new BorderLayout()); JScrollPane variablesScrollPane = new JScrollPane(); variablesScrollPane.setName("JScrollPane1"); variablesScrollPane.setPreferredSize(new Dimension(175, 131)); variablesScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); variablesScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); variablesScrollPane.setViewportView(variablesDisplayList); this.add(variablesScrollPane,"Center"); variablesDisplayList.setBorder(BorderFactory.createEmptyBorder()); variablesScrollPane.setBorder(BorderFactory.createEmptyBorder()); titleLabel.setText(GemCutter.getResourceString("CEP_Arguments_Title")); titleLabel.setBorder(new EmptyBorder(0,7,0,0)); this.add(titleLabel,"North"); } /** * Sets the display title * @param title */ public void setTitle(String title) { titleLabel.setText(title); } /** * Get the tooltip for an element in the list. * @param e MouseEvent the related mouse event * @return the associated tooltip */ @Override public String getToolTipText(MouseEvent e) { int index = variablesDisplayList.locationToIndex(e.getPoint()); if (-1 < index) { VariablePanel varPan = (VariablePanel)variablesDisplayList.getModel().getElementAt(index); return varPan.getToolTipText(); } else { return null; } } /** * Update the variable panels displayed by this JList * @param argVarPanels the variable panels which correspond to arguments * @param reorderingAllowed whether reordering is allowed. */ void updateVariablePanels(VariablePanel[] argVarPanels, boolean reorderingAllowed) { this.reorderingAllowed = reorderingAllowed; this.numArgVarPanels = argVarPanels.length; // update the JList to show the new data variablesDisplayList.setListData(argVarPanels.clone()); updatePreferredSize(); // Validate everything (for now - maybe restrict later) validate(); // Repaint the variablesDisplay repaint(); } /** * Update a variable panel displayed by this JList * @param panelIndex the index of the panel to update. * @param updatedVariablePanel the updated panel. */ void updateVariablePanel(int panelIndex, VariablePanel updatedVariablePanel) { VariablePanel originalPanel = (VariablePanel)variablesDisplayList.getModel().getElementAt(panelIndex); if (updatedVariablePanel.sameInfo(originalPanel)) { return; } List<Object> listData = getListData(); listData.set(panelIndex, updatedVariablePanel); // update the JList to show the new data variablesDisplayList.setListData(listData.toArray()); updatePreferredSize(); // Validate everything (for now - maybe restrict later) validate(); // Repaint the variablesDisplay repaint(); } /** * Return a new list containing the data in this JList. * @return List this JList's list data. */ private List<Object> getListData() { ListModel listModel = variablesDisplayList.getModel(); int numElements = listModel.getSize(); ArrayList<Object> listData = new ArrayList<Object>(numElements); for (int i = 0; i < numElements; i++) { listData.add(listModel.getElementAt(i)); } return listData; } /** * Add the listener for panel events in this component. * @param newPanelEventListener the new PanelEventListener. */ void addPanelEventListener(PanelEventListener newPanelEventListener) { this.panelEventListeners.add(newPanelEventListener); } /** * Selects the panel corresponding to the specified argument name * @param argumentName */ public void selectPanelForArgument(String argumentName) { List<Object> panels = getListData(); for (int i = 0; i < panels.size(); i++) { if (((VariablePanel)panels.get(i)).getIdentifier().getName().equals(argumentName)) { // Panel found variablesDisplayList.setSelectedIndex(i); break; } } } /** * Update the preferred size of the JList according to the sizes of the component variable panels */ private void updatePreferredSize() { // we only care about the height int numVarPanels = variablesDisplayList.getModel().getSize(); if (numVarPanels > 0) { Rectangle rect = variablesDisplayList.getCellBounds(0, numVarPanels - 1); variablesDisplayList.setPreferredSize(new Dimension(0, rect.height)); } else { variablesDisplayList.setPreferredSize(new Dimension(0, 0)); } } void clearSelection() { variablesDisplayList.clearSelection(); } void setDragEnabled(boolean enabled) { variablesDisplayList.setDragEnabled(enabled); } void setListTransferHandler(TransferHandler handler) { variablesDisplayList.setTransferHandler(handler); } VariablesDisplayList getListComponent() { return variablesDisplayList; } void addListFocusListener(FocusListener listener) { variablesDisplayList.addFocusListener(listener); } void setPopupMenuProvider(AdvancedCALEditor.IdentifierPopupMenuProvider popupProvider) { this.popupProvider = popupProvider; } }