/* $Id: RowSelector.java 19050 2011-02-23 19:34:02Z linus $
*******************************************************************************
* Copyright (c) 2009,2010 Contributors - see below
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Bob Tarling
*******************************************************************************
*/
// Copyright (c) 1996-2007 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
package org.argouml.core.propertypanels.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableCellEditor;
import org.apache.log4j.Logger;
import org.argouml.application.helpers.ResourceLoaderWrapper;
import org.argouml.core.propertypanels.model.IconIdentifiable;
import org.argouml.core.propertypanels.model.Named;
import org.argouml.i18n.Translator;
import org.argouml.kernel.Command;
import org.argouml.kernel.Project;
import org.argouml.kernel.ProjectManager;
import org.argouml.model.Model;
import org.argouml.model.UmlHelper;
import org.argouml.ui.ActionCreateContainedModelElement;
import org.argouml.ui.UndoableAction;
import org.tigris.gef.presentation.FigTextEditor;
import org.tigris.toolbar.toolbutton.ToolBox;
/**
* A control for displaying the contents of a list model elements in a panel
* that can be expanded to take maximum possible screen space or shrunk to
* minimum at the user discretion.
*
* @author Bob Tarling
* @since 0.29.2
*/
class RowSelector extends UmlControl
implements MouseListener, ListDataListener, ListSelectionListener, Expandable {
/**
* The logger
*/
private static final Logger LOG = Logger.getLogger(RowSelector.class);
/**
* class uid
*/
private static final long serialVersionUID = 3937183621483536749L;
/**
* The model element that is the target of this control
*/
private final Object target;
/**
* Identifies if the model element is a readonly modelelement
*/
private final boolean readonly;
private final List actions;
/**
* The scrollpane that will contain the list
*/
private ScrollList scroll;
/**
* The preferred size of the component when shrunk
*/
private final Dimension shrunkPreferredSize;
/**
* The preferred size of the component when expanded
*/
private final Dimension expandedPreferredSize;
/**
* The maximum size of the component when expanded
*/
private final Dimension expandedMaximumSize;
/**
* True if the component is expandable
*/
private final boolean expandable;
/**
* The current expanded state
*/
private boolean expanded = false;
/**
* The model element that is being moved. This is used because move
* effectively removes the selected model element and then adds it in a
* new place.
* The problem to the user is that when added the selection is lost.
* By recording the model element being moved the add event can detected
* when the element is added and mark it as selected.
*/
private MovedModelElement movedModelElement = new MovedModelElement();
/**
* The delete action that we must enable/disable
*/
private final DeleteAction deleteAction;
/**
* The remove action that we must enable/disable
*/
private final Action removeAction;
/**
* The delete action that we must enable/disable
*/
private final MoveUpAction moveUpAction;
/**
* The delete action that we must enable/disable
*/
private final MoveDownAction moveDownAction;
/**
* The delete action that we must enable/disable
*/
private final MoveTopAction moveTopAction;
/**
* The delete action that we must enable/disable
*/
private final MoveBottomAction moveBottomAction;
/**
* Constructor
* @param model The single item list model
*/
public RowSelector(DefaultListModel model) {
this(model, false, true);
}
/**
* Constructor
* @param model The single item list model
* @param expanded true if the control should be initially expanded
* @param expandable true if the control should be expandable
*/
public RowSelector(
final DefaultListModel model,
final boolean expanded,
final boolean expandable) {
super(new BorderLayout());
this.expandable = expandable;
Object metaType = null;
List metaTypes = null;
final Action addAction;
List<Action> newActions = null;
List<Command> additionalNewCommands = null;
final String propertyName;
if (model instanceof UMLModelElementListModel) {
// Temporary until SimpleListModel is used for all
target = ((UMLModelElementListModel) model).getTarget();
metaType = ((UMLModelElementListModel) model).getMetaType();
propertyName = null;
scroll = new OldScrollList(model, 1);
readonly = Model.getModelManagementHelper().isReadOnly(target);
metaTypes = null;
newActions = ((UMLModelElementListModel) model).getNewActions();
} else if (model instanceof SimpleListModel) {
target = ((SimpleListModel) model).getUmlElement();
propertyName = ((SimpleListModel) model).getPropertyName();
metaType = ((SimpleListModel) model).getMetaType();
metaTypes = ((SimpleListModel) model).getMetaTypes();
additionalNewCommands = ((SimpleListModel) model).getAdditionalCommands();
scroll = new ScrollListImpl(model, 1);
readonly = Model.getModelManagementHelper().isReadOnly(target);
} else {
// Temporary until SimpleListModel is used for all
propertyName = null;
target = null;
readonly = true;
}
assert (target != null);
if (metaTypes == null) {
metaTypes = new ArrayList();
metaTypes.add(metaType);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Creating list for " + target);
LOG.debug("model = " + model.getClass().getName());
LOG.debug("metatype = " + metaType);
LOG.debug("target = " + target);
}
add((JComponent) scroll);
shrunkPreferredSize = ((JComponent) scroll).getPreferredSize();
remove(((JComponent) scroll));
scroll = ScrollListFactory.create(model);
JScrollPane jscroll = ((JScrollPane) scroll);
add(jscroll);
expandedPreferredSize = jscroll.getPreferredSize();
expandedMaximumSize = jscroll.getMaximumSize();
jscroll.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
if (model instanceof SimpleListModel
&& ((SimpleListModel) model).getAddCommand() != null) {
removeAction =
new RemoveAction(scroll.getList(), ((SimpleListModel) model));
addAction =
new AddAction(((SimpleListModel) model).getAddCommand());
} else {
removeAction = null;
addAction = null;
}
actions = new ArrayList<Action>(6);
if (!expandable && !expanded) {
jscroll.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_NEVER);
deleteAction = null;
moveUpAction = null;
moveDownAction = null;
moveTopAction = null;
moveBottomAction = null;
if (!readonly) {
// Create popup tool if we have a single row
for (Object meta : metaTypes) {
if (Model.getUmlFactory().isContainmentValid(
meta, target)) {
final String label =
"button.new-"
+ Model.getMetaTypes().getName(meta).toLowerCase();
final Action createAction =
new ActionCreateContainedModelElement(
meta,
target,
propertyName,
label);
actions.add(createAction);
}
}
if (newActions != null) {
actions.addAll(newActions);
}
if (additionalNewCommands != null
&& !additionalNewCommands.isEmpty()) {
for (Command cmd : additionalNewCommands) {
if (cmd instanceof IconIdentifiable
&& cmd instanceof Named) {
actions.add(new CommandAction(
cmd,
((Named)cmd).getName(),
((IconIdentifiable)cmd).getIcon()));
} else {
actions.add(new CommandAction(cmd));
}
}
}
if (!actions.isEmpty()) {
final JPanel buttonPanel =
createSingleButtonPanel(actions);
add(buttonPanel, BorderLayout.WEST);
}
}
} else {
if (!readonly) {
// TODO: Lets build this into a separate buildToolbar method
// Create add and remove buttons if needed first
if (addAction != null) {
actions.add(addAction);
}
if (removeAction != null) {
actions.add(removeAction);
deleteAction = null;
} else {
deleteAction = new DeleteAction();
actions.add(deleteAction);
}
// then any new buttons
List<Action> createActions = new ArrayList<Action>();
for (Object meta : metaTypes) {
if (Model.getUmlFactory().isContainmentValid(
meta, target)) {
final String label =
"button.new-"
+ Model.getMetaTypes().getName(meta).toLowerCase();
final Action createAction =
new ActionCreateContainedModelElement(
meta,
target,
label);
createActions.add(createAction);
}
}
if (additionalNewCommands != null
&& !additionalNewCommands.isEmpty()) {
for (Command cmd : additionalNewCommands) {
if (cmd instanceof IconIdentifiable && cmd instanceof Named) {
createActions.add(new CommandAction(cmd, ((Named)cmd).getName(), ((IconIdentifiable)cmd).getIcon()));
} else {
createActions.add(new CommandAction(cmd));
}
}
}
if (createActions.size() > 2) {
actions.add(createActions.toArray());
} else {
actions.addAll(createActions);
}
if (Model.getUmlHelper().isMovable(metaType)) {
moveUpAction = new MoveUpAction();
moveDownAction = new MoveDownAction();
moveTopAction = new MoveTopAction();
moveBottomAction = new MoveBottomAction();
} else {
moveUpAction = null;
moveDownAction = null;
moveTopAction = null;
moveBottomAction = null;
}
} else {
moveUpAction = null;
moveDownAction = null;
moveTopAction = null;
moveBottomAction = null;
deleteAction = null;
}
this.addMouseListener(this);
if (!Model.getModelManagementHelper().isReadOnly(target)) {
if (deleteAction != null) {
getList().addListSelectionListener(deleteAction);
}
if (removeAction != null) {
getList().addListSelectionListener(this);
}
// TODO: We should really test the model instead for this
// but we have no API yet.
// Can we just check if the collection to build the JList
// control implements the List interface?
if (Model.getUmlHelper().isMovable(metaType)) {
getList().addListSelectionListener(moveUpAction);
getList().addListSelectionListener(moveDownAction);
getList().addListSelectionListener(moveTopAction);
getList().addListSelectionListener(moveBottomAction);
}
}
getList().addMouseListener(this);
getModel().addListDataListener(this);
}
}
/**
* Make sure the control is always a fixed height
* @return the minimum size as the height of one row in a JList
*/
public Dimension getMinimumSize() {
return shrunkPreferredSize;
}
/**
* Make sure the control is always a fixed height
* @return the maximum size as the height of one row in a JList
*/
public Dimension getMaximumSize() {
Dimension size = super.getMaximumSize();
if (expanded) {
size.height = expandedMaximumSize.height;
} else {
size.height = shrunkPreferredSize.height;
}
return size;
}
/**
* @return the preferred size as the height of one row in a JList
*/
public Dimension getPreferredSize() {
if (expanded) {
return expandedPreferredSize;
} else {
return shrunkPreferredSize;
}
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
JPopupMenu popup = new JPopupMenu();
for (Object action : actions) {
if (action instanceof Action) {
popup.add((Action) action);
}
}
if (moveTopAction != null) {
popup.add(moveTopAction);
popup.add(moveBottomAction);
popup.add(moveUpAction);
popup.add(moveDownAction);
}
if (popup.getComponentCount() > 0) {
popup.show(this, e.getX(), e.getY());
}
e.consume();
}
}
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
JPopupMenu popup = new JPopupMenu();
for (Object action : actions) {
if (action instanceof Action) {
popup.add((Action) action);
}
}
if (moveTopAction != null) {
popup.add(moveTopAction);
popup.add(moveBottomAction);
popup.add(moveUpAction);
popup.add(moveDownAction);
}
if (popup.getComponentCount() > 0) {
popup.show(this, e.getX(), e.getY());
}
e.consume();
}
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
public JComponent getExpansion() {
List<Action> flatActions = new ArrayList<Action>();
for (Object o : actions) {
if (o instanceof Action) {
flatActions.add((Action) o);
} else {
Object[] oa = (Object[]) o;
for (int j = 0; j < oa.length; ++j) {
flatActions.add((Action) oa[j]);
}
}
}
final ToolBox tb =
new ToolBox(2, flatActions.size() / 2 + flatActions.size() % 2, true);
for (int i = 0; i < flatActions.size() / 2 + flatActions.size() % 2; ++i) {
tb.add((Action) flatActions.get(i));
}
if (moveUpAction != null) {
tb.add(moveUpAction);
}
if (moveTopAction != null) {
tb.add(moveTopAction);
}
if (flatActions.size() % 2 == 1) {
tb.add(new JPanel());
}
for (int i = flatActions.size() / 2 + flatActions.size() % 2; i < flatActions.size(); ++i) {
tb.add((Action) flatActions.get(i));
}
if (moveDownAction != null) {
tb.add(moveDownAction);
}
if (moveBottomAction != null) {
tb.add(moveBottomAction);
}
JComponent expander = new JPanel(
new FlowLayout(FlowLayout.RIGHT));
expander.add(tb);
return expander;
}
public boolean isExpanded() {
return expanded;
}
public boolean isExpandable() {
return expandable;
}
/**
* Remove all the listeners that were added in the constructor
*/
public void removeNotify() {
if (!readonly) {
getList().removeListSelectionListener(this);
getList().removeListSelectionListener(deleteAction);
if (moveUpAction != null) {
getList().removeListSelectionListener(moveUpAction);
getList().removeListSelectionListener(moveDownAction);
getList().removeListSelectionListener(moveTopAction);
getList().removeListSelectionListener(moveBottomAction);
getList().removeMouseListener(this);
}
}
this.removeMouseListener(this);
getModel().removeListDataListener(this);
if (getModel() instanceof UMLModelElementListModel) {
((UMLModelElementListModel) getModel()).removeModelEventListener();
} else {
((SimpleListModel) getModel()).removeModelEventListener();
}
}
/**
* Add a listener for selection changes to the list
* @param listener the listener
*/
public void addListSelectionListener(ListSelectionListener listener) {
scroll.getList().addListSelectionListener(listener);
}
/**
* Add a listener for selection changes to the list
* @param listener the listener
*/
public void removeListSelectionListener(ListSelectionListener listener) {
scroll.getList().removeListSelectionListener(listener);
}
/**
* Get the JList that is wrapped in this component
* @return the JList
*/
JList getList() {
return scroll.getList();
}
/**
* Clear all selections in the list
*/
public void clearSelection() {
getList().clearSelection();
}
/**
* Get the list model
* @return the ListModel
*/
private ListModel getModel() {
return (ListModel) scroll.getList().getModel();
}
public void contentsChanged(ListDataEvent e) {
}
public void intervalAdded(ListDataEvent e) {
if (e.getIndex0() == e.getIndex1()
&& getModel().getElementAt(e.getIndex0())
== movedModelElement.getElement()) {
LOG.info("Setting attribute to selected");
final Object element = movedModelElement.getElement();
movedModelElement.setElement(null);
getList().setSelectedValue(element, true);
// Pushing this to the end of the AWT thread seems to be the only
// way to get this to update correctly
Runnable doWorkRunnable = new Runnable() {
public void run() {
getList().setSelectedValue(element, true);
}
};
SwingUtilities.invokeLater(doWorkRunnable);
}
}
public void intervalRemoved(ListDataEvent e) {
}
public void valueChanged(ListSelectionEvent e) {
if (removeAction != null) {
removeAction.setEnabled(getList().getSelectedIndex() > -1);
}
}
/**
* This action deletes the model elements that are selected in the JList
*/
private class DeleteAction extends UndoableAction
implements ListSelectionListener {
/**
* The class uid
*/
private static final long serialVersionUID = -1466007194555518247L;
/**
* Construct the Action
*/
public DeleteAction() {
super(Translator.localize("menu.popup.delete"),
ResourceLoaderWrapper.lookupIconResource("DeleteFromModel"));
setEnabled(false);
}
/**
* Set the action as enabled when any row is selected
* @param e the event
*/
public void valueChanged(ListSelectionEvent e) {
setEnabled(getList().getSelectedIndex() > -1);
}
/***
* Perform the action
* @param e the event
*/
public void actionPerformed(ActionEvent ae) {
super.actionPerformed(ae);
// TODO Part of this is copied from ActionDeleteModelElement. We
// maybe need some subclass for common code.
KeyboardFocusManager focusManager =
KeyboardFocusManager.getCurrentKeyboardFocusManager();
Component focusOwner = focusManager.getFocusOwner();
if (focusOwner instanceof FigTextEditor) {
// TODO: Probably really want to cancel editing
//((FigTextEditor) focusOwner).cancelEditing();
((FigTextEditor) focusOwner).endEditing();
} else if (focusOwner instanceof JTable) {
JTable table = (JTable) focusOwner;
if (table.isEditing()) {
TableCellEditor ce = table.getCellEditor();
if (ce != null) {
ce.cancelCellEditing();
}
}
}
final Project p = ProjectManager.getManager().getCurrentProject();
final Object[] selectedValues = getList().getSelectedValues();
p.moveToTrash(Arrays.asList(selectedValues));
}
}
/**
* This action deletes the model elements that are selected in the JList
*/
private class MoveUpAction extends UndoableAction implements ListSelectionListener {
/**
* The class uid
*/
private static final long serialVersionUID = 92834374054221267L;
/**
* Construct the action
*/
MoveUpAction() {
super(Translator.localize("menu.popup.moveup"),
ResourceLoaderWrapper.lookupIconResource("MoveUp"));
setEnabled(false);
}
/**
* Set the action as enabled when any row other then the first is selected
* @param e the event
*/
public void valueChanged(ListSelectionEvent e) {
setEnabled(getList().getSelectedIndex() > 0);
}
/***
* Perform the action
* @param e the event
*/
@Override
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
movedModelElement.setElement(getList().getSelectedValues()[0]);
assert (movedModelElement != null);
Model.getUmlHelper().move(
target,
movedModelElement.getElement(),
UmlHelper.Direction.UP);
}
}
/**
* This action deletes the model elements that are selected in the JList
*/
private class MoveDownAction extends UndoableAction
implements ListSelectionListener {
/**
* The class uid
*/
private static final long serialVersionUID = -4898882853644454510L;
/**
* Construct the action
*/
MoveDownAction() {
super(Translator.localize("menu.popup.movedown"),
ResourceLoaderWrapper.lookupIconResource("MoveDown"));
setEnabled(false);
}
/**
* Set the action as enabled when any row other then the last is selected
* @param e the event
*/
public void valueChanged(ListSelectionEvent e) {
final int index = getList().getSelectedIndex();
setEnabled(index > -1 && index < getModel().getSize() - 1);
}
/***
* Perform the action
* @param e the event
*/
@Override
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
movedModelElement.setElement(getList().getSelectedValues()[0]);
assert (movedModelElement != null);
Model.getUmlHelper().move(
target,
movedModelElement.getElement(),
UmlHelper.Direction.DOWN);
}
}
/**
* This action deletes the model elements that are selected in the JList
*/
private class MoveTopAction extends UndoableAction
implements ListSelectionListener {
/**
* The class uid
*/
private static final long serialVersionUID = -2767622732024791396L;
/**
* Construct the action
*/
MoveTopAction() {
super(Translator.localize("menu.popup.movetotop"),
ResourceLoaderWrapper.lookupIconResource("MoveTop"));
setEnabled(false);
}
/**
* Set the action as enabled when any row other then the first is selected
* @param e the event
*/
public void valueChanged(ListSelectionEvent e) {
setEnabled(getList().getSelectedIndex() > 0);
}
/***
* Perform the action
* @param e the event
*/
@Override
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
movedModelElement.setElement(getList().getSelectedValues()[0]);
assert (movedModelElement != null);
Model.getUmlHelper().move(
target,
movedModelElement.getElement(),
UmlHelper.Direction.TOP);
}
}
/**
* This action moves up the model elements that are selected in the JList
*/
private class MoveBottomAction extends UndoableAction
implements ListSelectionListener {
/**
* The class uid
*/
private static final long serialVersionUID = -3459350012282215204L;
/**
* Construct the action
*/
MoveBottomAction() {
super(Translator.localize("menu.popup.movetobottom"),
ResourceLoaderWrapper.lookupIconResource("MoveBottom"));
setEnabled(false);
}
/**
* Set the action as enabled when any row other then the last is selected
* @param e the event
*/
public void valueChanged(ListSelectionEvent e) {
final int index = getList().getSelectedIndex();
setEnabled(index > -1 && index < getModel().getSize() - 1);
}
/***
* Perform the action
* @param e the event
*/
@Override
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
movedModelElement.setElement(getList().getSelectedValues()[0]);
assert (movedModelElement != null);
Model.getUmlHelper().move(
target,
movedModelElement.getElement(),
UmlHelper.Direction.BOTTOM);
}
}
private class MovedModelElement {
private Object element;
public Object getElement() {
return element;
}
public void setElement(Object element) {
LOG.info("Setting moved model element to " + element);
this.element = element;
}
}
private static class AddAction extends UndoableAction {
private Command command;
public AddAction(Command command) {
super("", ResourceLoaderWrapper.lookupIcon("Add"));
this.command = command;
}
@Override
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
command.execute();
}
}
private static class RemoveAction extends UndoableAction {
private final SimpleListModel simpleListModel;
private final JList list;
public RemoveAction(JList list, SimpleListModel model) {
super("", ResourceLoaderWrapper.lookupIcon("Remove"));
this.simpleListModel = model;
this.list = list;
}
@Override
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
final Object objectToRemove = list.getSelectedValue();
if (objectToRemove!= null) {
Command command =
simpleListModel.getRemoveCommand(objectToRemove);
command.execute();
} else {
LOG.warn("No selected object was found in the list control - we shouldn't be able to get here");
}
}
}
private static class CommandAction extends UndoableAction {
private final Command command;
public CommandAction(Command cmd) {
super("", ResourceLoaderWrapper.lookupIcon("Remove"));
this.command = cmd;
}
public CommandAction(Command cmd, String name, Icon icon) {
super(name, icon);
this.command = cmd;
}
@Override
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
command.execute();
}
}
}