TiedNumberPanel.java :  » Math » migen » uk » ac » lkl » migen » system » expresser » ui » Java Open Source

Java Open Source » Math » migen 
migen » uk » ac » lkl » migen » system » expresser » ui » TiedNumberPanel.java
package uk.ac.lkl.migen.system.expresser.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
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.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.border.Border;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import uk.ac.lkl.common.ui.jft.ModalPaperNoteCallOut;
import uk.ac.lkl.common.ui.jft.Stencil;
import uk.ac.lkl.common.util.ID;
import uk.ac.lkl.common.util.IDFactory;
import uk.ac.lkl.common.util.IDObject;
import uk.ac.lkl.common.util.ObjectWithID;
import uk.ac.lkl.common.util.event.SourcedUpdateSupport;
import uk.ac.lkl.common.util.event.UpdateEvent;
import uk.ac.lkl.common.util.event.UpdateListener;
import uk.ac.lkl.common.util.expression.Expression;
import uk.ac.lkl.common.util.expression.ExpressionList;
import uk.ac.lkl.common.util.expression.LocatedExpression;
import uk.ac.lkl.common.util.expression.ModifiableOperation;
import uk.ac.lkl.common.util.expression.Operator;
import uk.ac.lkl.common.util.expression.operator.IntegerAdditionOperator;
import uk.ac.lkl.common.util.expression.operator.IntegerMultiplicationOperator;
import uk.ac.lkl.common.util.expression.operator.IntegerSubtractionOperator;
import uk.ac.lkl.common.util.value.IntegerValue;
import uk.ac.lkl.common.util.value.Value;
import uk.ac.lkl.migen.system.ExpresserLauncher;
import uk.ac.lkl.migen.system.MiGenConfiguration;
import uk.ac.lkl.migen.system.expresser.model.AttributeHandle;
import uk.ac.lkl.migen.system.expresser.model.ExpresserModel;
import uk.ac.lkl.migen.system.expresser.model.ExpresserModelSlaveCopy;
import uk.ac.lkl.migen.system.expresser.model.Walker;
import uk.ac.lkl.migen.system.expresser.model.shape.block.BlockShape;
import uk.ac.lkl.migen.system.expresser.model.shape.block.GroupShape;
import uk.ac.lkl.migen.system.expresser.model.shape.block.PatternShape;
import uk.ac.lkl.migen.system.expresser.model.tiednumber.TiedNumberExpression;
import uk.ac.lkl.migen.system.expresser.model.tiednumber.UnspecifiedTiedNumberExpression;
import uk.ac.lkl.migen.system.expresser.ui.ecollaborator.ActivityDocument;
import uk.ac.lkl.migen.system.expresser.ui.uievent.TiedNumberColorEvent;
import uk.ac.lkl.migen.system.expresser.ui.uievent.TiedNumberDragStartEvent;
import uk.ac.lkl.migen.system.expresser.ui.uievent.TiedNumberMenuItemEvent;
import uk.ac.lkl.migen.system.expresser.ui.uievent.TiedNumberMenuPopupEvent;
import uk.ac.lkl.migen.system.expresser.ui.uievent.TiedNumberSlaveValueChangedEvent;
import uk.ac.lkl.migen.system.expresser.ui.uievent.TiedNumberValueEditedEvent;
import uk.ac.lkl.migen.system.expresser.ui.uievent.UIEventManager;
import uk.ac.lkl.migen.system.util.MiGenUtilities;

public class TiedNumberPanel<V extends Value<V>> extends JPanel 
       implements Transferable, DragGestureListener, DragSourceListener, IDObject, ObjectWithID {

    public static final String NAME_IF_NO_NAME_GIVEN = TiedNumberExpression.getUnnamedName();

    // FIXME: @Hack(who = "MM", why = "WOZ", issues = 0)
    // used for WOZ to fully lockdown expresser (i.e. do not allow further
    // unlocking)
    public static boolean expresserFullyLockedDown = false;

    private UpdateListener<Expression<IntegerValue>> tiedNumberUpdateListener =
  new UpdateListener<Expression<IntegerValue>>() {

  public void objectUpdated(UpdateEvent<Expression<IntegerValue>> e) {
      String currentTextValue = valueTextField.getText();
      String tiedNumberTextValue = tiedNumber.evaluate().toString();
      // following causes
      // Exception in thread "AWT-EventQueue-0"
      // java.lang.IllegalStateException: Attempt to mutate in
      // notification
      // if the value was edited but is the same integer (e.g.
      // 1 and 01).
      if (!tiedNumberTextValue.equals(currentTextValue)) {
    try {
        valueTextField.setText(tiedNumberTextValue);
        if (readOnly &&
      TiedNumberPanel.this.isDisplayable()) {
      // TODO: figure out why some non-displayable
      // tied number panels listen to this
      UIEventManager.processEvent(
        new TiedNumberSlaveValueChangedEvent(getIdentityForLogMessage()));
//      logMessage("Slave tied number value changed.");
        }
//        invalidate();
    } catch (java.lang.IllegalStateException ignore) {
        // don't care if editing, for example, 03 changes it to 3
    }
      }
      updateSize();
      // following attempt to fix the exception made it worse
      // int currentValue = tiedNumber.evaluate().getInt();
      // try {
      // int newValue = Integer.parseInt(currentTextValue);
      // if (newValue == currentValue) {
      // return; // change already noted
      // } else {
      // valueTextField.setText(Integer.toString(newValue));
      // }
      // } catch (NumberFormatException numberFormatException) {
      // return;
      // }
  }

    };

    private UpdateListener<TiedNumberExpression<IntegerValue>> lockStatusUpdateListener =
  new UpdateListener<TiedNumberExpression<IntegerValue>>() {

  public void objectUpdated(
    UpdateEvent<TiedNumberExpression<IntegerValue>> e) {
      reflectChangableStatus();
      if (!tiedNumber.isLocked()) {
    // was shared earlier
    MasterSlaveUniverseMicroworldPanel masterSlaveUniverseMicroworldPanel =
        getMasterSlaveUniverseMicroworldPanel();
    if (masterSlaveUniverseMicroworldPanel != null) {
//        BlockShapeCanvasPanel slavePanel =
//      masterSlaveUniverseMicroworldPanel.getSlavePanel();
//        if (slavePanel.isAncestorOf(TiedNumberPanel.this)) {
//      ExpresserModel slaveModel = slavePanel.getModel();
//      if (slaveModel != null) {
//          final ExpresserModelSlaveCopy expresserModelSlaveCopy =
//        (ExpresserModelSlaveCopy) slaveModel;
//          TiedNumberExpression<?> slaveCopy =
//        expresserModelSlaveCopy.getSlaveCopy(tiedNumber);
//          if (slaveCopy != tiedNumber) {
//        setTiedNumber((TiedNumberExpression<IntegerValue>) slaveCopy);
//        // use a fresh slave copy if the master changes
//        final TiedNumberExpression<IntegerValue> originalMaster =
//            tiedNumber;
//        UpdateListener<Expression<IntegerValue>> updateListener =
//            new UpdateListener<Expression<IntegerValue>>() {
//
//            public void objectUpdated(
//              UpdateEvent<Expression<IntegerValue>> e) {
//          TiedNumberExpression<?> slaveCopy =
//              expresserModelSlaveCopy.getSlaveCopy(originalMaster);
//          setTiedNumber((TiedNumberExpression<IntegerValue>) slaveCopy);
//            }
//
//        };
//        tiedNumber.addUpdateListener(updateListener);
//        slaveModel.setDirtyModel(true);
//          }
//      }
//        } else {
      masterSlaveUniverseMicroworldPanel.getModel().setDirtyModel(true);
//        }
    }
      }
  }

    };

    // Some tied number panels are not to be dragged (e.g. those in the slave
    // universe)
    private boolean dragEnabled = false;

    // the tied number this panel represents
    protected TiedNumberExpression<IntegerValue> tiedNumber;

    // text field for the value of the number
    protected JTextField valueTextField = new JTextField();

    // TODO: determine if this is now obsolete
    // and only valueTextField is needed
    protected JPanel valueTextFieldPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));

    // this is where the name or description of the tied number
    // is displayed and can be edited
    // only used with the old horizontal layout
    private JTextField nameField = null;
    
    // vertical layout doesn't support implicit editing
    // of names but must be explicitly requested
    // via a menu item
    private JLabel nameLabel = null;
    
    // when editing the name the following
    // replaces the nameLabel
    private JTextField nameEditField = null;

    private JPanel nameFieldPanel;
    
    private BorderLayout borderLayout;

    private static Color defaultUnlockedFieldBackgroundColor =
  new Color(255, 255, 200); // light yellow

    private static Color defaultLockedFieldBackgroundColor =
  new Color(220, 220, 220); // lighter gray than Color.LIGHT_GRAY

    /**
     * If this is replaced by an expression replacement is set
     */
    private Expression<IntegerValue> replacement = null;

    /**
     * ExpressionPanel will want to know if this is replaced
     */
    private SourcedUpdateSupport<TiedNumberPanel<V>> updateSupport =
  new SourcedUpdateSupport<TiedNumberPanel<V>>(this);

    // automates changing the value of this tied number
    private JButton playButton = null;

    // for drag and drop need a flavor for TiedNumberPanels
    static public DataFlavor tiedNumberPanelFlavor = createTiedNumberPanelDataFlavor();

    private static DataFlavor[] dataFlavors = { tiedNumberPanelFlavor };

    // by default copy the panel when dragging but 
    // becomes move when left on the canvas
    private int transferMode = TransferHandler.COPY;

    private DragGestureRecognizer dragGestureRecognizer = null;

    private boolean dropEnabled;

    protected DocumentListener valueEditedListener = new DocumentListener() {

  public void changedUpdate(DocumentEvent e) {
  }

  public void insertUpdate(DocumentEvent e) {
      acceptEditedValue();
      if (!readOnly) {
    UIEventManager.processEvent(
      new TiedNumberValueEditedEvent(getIdentityForLogMessage(), true));
      }
  }

  public void removeUpdate(DocumentEvent e) {
      acceptEditedValue();
      if (!readOnly) {
    UIEventManager.processEvent(
      new TiedNumberValueEditedEvent(getIdentityForLogMessage(), false));
      }
  }

    };

    private boolean readOnly;
    
    private Boolean containedInMasterPanel = null;

    // used to maintain the location that the slave of this was moved to
    private Point slaveLocation;

    // each panel needs an id for logging since the same number can be inside
    // multiple panels
    private ID id;
    
    private ExpressionPanel<IntegerValue> dragee = null;

    private DragSource dragSource = null;

    private DisplayDragSourceDragSourceMotionListener dragSourceMotionListener =
  null;

    private UpdateListener<TiedNumberExpression<IntegerValue>> nameFieldUpdateListener =
  null;

    private UpdateListener<TiedNumberExpression<IntegerValue>> displayModeUpdateListener =
  null;

    private TiedNumberPanel<V> numberPanelStartingDrag = null;

    public static Font defaultFont = new Font("Courier", Font.PLAIN, 14);

    public static final Border DEFAULT_BORDER_HORIZONTAL = 
  BorderFactory.createEtchedBorder();
    
    public static final Border DEFAULT_BORDER_VERTICAL = 
  BorderFactory.createEtchedBorder(Color.BLUE, Color.LIGHT_GRAY);
      
    // defining the border size
    private static final int VERTICAL_GAP = 3;
    private static final int HORIZONTAL_GAP = 3;

    // font for value
    public static Font VALUE_FONT = null;

    // this needs to be a fixed width font to avoid lots of white space
    // appearing to the right of the name
    public static Font NAME_FONT_HORIZONTAL = new Font("Courier", Font.BOLD, 20);
    
    // following is font used by Windows for title bars
    // and available on Macs 
    public static Font NAME_FONT_VERTICAL = null;

    protected static final Color NAME_IN_USE_COLOR = Color.MAGENTA;
    protected static final Color NAME_IMPROPER_COLOR = Color.RED;
    protected static final Color NAME_NOT_GIVEN_COLOR = Color.PINK;
    // text colour when vertical looks more like a typical title bar
    protected static final Color NAME_OK_COLOR_VERTICAL = Color.WHITE;
    protected static final Color NAME_OK_COLOR_HORIZONTAL = Color.BLACK;
    
    // use title bar default colour in Windows XP
    public static final Color NAME_BACKGROUND_COLOR_VERTICAL = new Color(2, 105, 254);

    public TiedNumberPanel(final TiedNumberExpression<IntegerValue> tiedNumber) {
  this(tiedNumber, false);
    }

    public TiedNumberPanel(final TiedNumberExpression<IntegerValue> tiedNumber, boolean readOnly) {
  this.readOnly = readOnly;
  id = IDFactory.newID(this);
  nameFieldPanel = new JPanel(new BorderLayout());
  borderLayout = new BorderLayout();
  setLayout(borderLayout);
  setTiedNumber(tiedNumber);
  displayModeUpdated();
//  if (!readOnly) {
//      addHierarchyListener();
//  }
  // needed even if readOnly but many menu items will be disabled
  // change in policy with Issue 946 that menu pops up only if not read only
  if (!readOnly) {
      addPopupMenuOnClick();
  }
    }
    
    public String getIdName() {
  return "TiedNumberPanel";
    }

//    protected void addHierarchyListener() {
//  addHierarchyListener(new HierarchyListener() {
//
//      public void hierarchyChanged(HierarchyEvent e) {
//    displayModeUpdated();
//    setDropEnabled(isOnACanvas());
//      }
//  }
//  );
//    }

    public void setTiedNumber(final TiedNumberExpression<IntegerValue> tiedNumber) {
  removeAll();
  this.tiedNumber = tiedNumber;
  String name = tiedNumber.getName();
  if (!editingName()) {
      if (nameLabel != null) {
    nameFieldPanel.remove(nameLabel);
      }
      // put spaces on both sides to look nicer
      nameLabel = new JLabel(" " + name + " " ); // + authorInfo()
      nameLabel.setBackground(NAME_BACKGROUND_COLOR_VERTICAL);
      nameLabel.setForeground(NAME_OK_COLOR_VERTICAL);
      nameLabel.setFont(nameFont());
      nameFieldPanel.add(nameLabel, BorderLayout.CENTER);
  }
  tiedNumber.addLockStatusUpdateListener(lockStatusUpdateListener);
  addTiedNumberValueUpdateListener();
  addDisplayModeUpdateListener();
  addNameFieldUpdateListener();
  valueTextField.setBackground(defaultUnlockedFieldBackgroundColor);
  String newTextValue = tiedNumber.getValue().toString();
  String oldTextValue = valueTextField.getText();
  if (!newTextValue.equals(oldTextValue)) {
      valueTextField.setText(newTextValue);
  }
  addDocumentListener();
  reflectChangableStatus();
  valueTextFieldPanel.removeAll();
  valueTextFieldPanel.add(valueTextField, BorderLayout.CENTER);
  // was !readOnly but see
  // http://code.google.com/p/migen/source/detail?r=2813
  setDragEnabled(true);
  displayModeUpdated();
    }

//    private String authorInfo() {
//  if (MiGenConfiguration.isShowAuthorOfVariables() && 
//      tiedNumber.displayAuthorInfo()) {
//      return MiGenUtilities.getLocalisedMessage("AuthorStart") + 
//             tiedNumber.getAuthor() + 
//             MiGenUtilities.getLocalisedMessage("AuthorEnd");
//  } else {
//      return "";
//  }
//    }

    protected void reflectChangableStatus() {
  boolean changeable = !tiedNumber.isLocked();
  setEditable(changeable);
  updateValueTextFieldBackground();
  setBorderAndFontForValueOnly();
    }

    protected void setEditable(boolean changeable) {
  valueTextField.setEditable(changeable && okToEditValue());
    }

    private void updateValueTextFieldBackground() {
  if (tiedNumber.isLocked()) {
      valueTextField.setBackground(defaultUnlockedFieldBackgroundColor);    
  } else {    
      valueTextField.setBackground(defaultLockedFieldBackgroundColor);
  }
    }

    protected void addDocumentListener() {
  valueTextField.getDocument().addDocumentListener(valueEditedListener);
    }

    protected void setBorderAndFontForValueOnly() {
  valueTextField.setFont(getValueFont());
  valueTextField.setBorder(defaultBorder());
    }

    private void setBorderAndFontForNameAndValue() {
  valueTextField.setFont(getValueFont()); 
  valueTextField.setBorder(null);
  if (nameField != null) {
      nameField.setBorder(null);
  }
    }

    protected void addPopupMenuOnClick() {
  addMouseListener(new MouseListener() {

      public void mouseClicked(MouseEvent e) {
      }

      public void mouseEntered(MouseEvent e) {
    TiedNumberPanel.this.setCursor(new Cursor(Cursor.HAND_CURSOR));
      }

      public void mouseExited(MouseEvent e) {
    TiedNumberPanel.this.setCursor(Cursor.getDefaultCursor());
      }

      public void mousePressed(MouseEvent e) {
      }

      public void mouseReleased(MouseEvent e) {
    addPopupMenu();
      }

  });
    }

    public JPopupMenu addPopupMenu() {
  UIEventManager.processEvent(new TiedNumberMenuPopupEvent(getIdentityForLogMessage(), "Main"));
//  logMenuPoppedUp("Main");
  JPopupMenu menu = new JPopupMenu();
  menu.add(lockUnlockMenuItem());
  menu.add(showNameAndValueMenuItem());
  menu.add(showNameOnlyMenuItem());
  menu.add(showValueOnlyMenuItem());
  menu.add(editNameMenuItem());
  menu.add(removeThisMenuItem());
  menu.add(copyThisMenuItem());
  JMenuItem menuItem = showContainersMenuItem(false, false);
  if (menuItem != null) {
      menu.add(menuItem);
  }
  if (MiGenConfiguration.isAddMenuItemToShowAnythingUsingTiedNumber()) {
      menuItem = showContainersMenuItem(true, false);
      if (menuItem != null) {
    menu.add(menuItem);
      }
  }
  menu.show(this, 0, 0);
  return menu;
    }
    
    protected JMenuItem lockUnlockMenuItem() {
  final ExpresserModel model = getModel();
  JMenuItem item;
  if (!getTiedNumberExpression().isLocked()) {
      final String label = MiGenUtilities.getLocalisedMessage("Lock");
      item = new JMenuItem(label);
      item.addActionListener(new ActionListener() {

    
    public void actionPerformed(ActionEvent e) {
        UIEventManager.processEvent(
          new TiedNumberMenuItemEvent(getIdentityForLogMessage(), label));
//        logActionPerformed(label);
        valueTextField.setEditable(false);
        // remove if it is there
        model.removeTaskVariable(getTiedNumberExpression()); 
        displayModeUpdated();
    }
      });
  } else {
      final String label =
    MiGenUtilities.getLocalisedMessage("Unlock");
      item = new JMenuItem(label);
      if (getTiedNumberExpression().isKeyAvailable()) {
    item.addActionListener(new ActionListener() {

        
        public void actionPerformed(ActionEvent e) {
      UIEventManager.processEvent(
        new TiedNumberMenuItemEvent(getIdentityForLogMessage(), label));
//      logActionPerformed(label);
      // following will also set the flag
      model.addTaskVariable(getTiedNumberExpression());
      valueTextField.setEditable(okToEditValue());
      displayModeUpdated();
        }
    });
      } else {
    item.setEnabled(false);
      }
  }
  if (isReadOnly() || expresserFullyLockedDown || model == null) {
      // model can be null if canvas is part of an activity document
      item.setEnabled(false);
  }
  return item;
    }
    
    protected JMenuItem showNameAndValueMenuItem() {
  final ExpresserModel model = getModel();
  final int displayMode = getTiedNumberExpression().getDisplayMode();
  final boolean unnamed = !tiedNumber.isNamed();
  final String showOrGiveName =
        unnamed ? MiGenUtilities.getLocalisedMessage("GiveName")
          : MiGenUtilities.getLocalisedMessage("ShowNameValue");
  JMenuItem item = new JMenuItem(showOrGiveName);
  if (displayMode == TiedNumberExpression.DISPLAY_NAME_AND_VALUE) {
      item.setEnabled(false);
  } else {
      item.addActionListener(new ActionListener() {

    
    public void actionPerformed(ActionEvent e) {
        UIEventManager.processEvent(
          new TiedNumberMenuItemEvent(getIdentityForLogMessage(), showOrGiveName));
//        logActionPerformed(showOrGiveName);
        tiedNumber.setDisplayMode(TiedNumberExpression.DISPLAY_NAME_AND_VALUE);
        if (unnamed) {
      editName();
        }
        ExpresserLauncher.repaint();
    }
      });
  }
  if (isReadOnly() || model == null) {
      item.setEnabled(false);
  }
  return item;
    }
    
    protected JMenuItem showNameOnlyMenuItem() {
  final String showNameLabel =
      MiGenUtilities.getLocalisedMessage("ShowNameOnly");
  final ExpresserModel model = getModel();
  final int displayMode = getTiedNumberExpression().getDisplayMode();
  final boolean unnamed = !tiedNumber.isNamed();
  JMenuItem item = new JMenuItem(showNameLabel);
  if (displayMode == TiedNumberExpression.DISPLAY_ONLY_NAME || unnamed) {
      item.setEnabled(false);
  } else {
      item.addActionListener(new ActionListener() {

    public void actionPerformed(ActionEvent e) {
        UIEventManager.processEvent(
          new TiedNumberMenuItemEvent(getIdentityForLogMessage(), showNameLabel));
//        logActionPerformed(showNameLabel);
        tiedNumber.setDisplayMode(TiedNumberExpression.DISPLAY_ONLY_NAME);    
        // so property mirrors are updated
        ExpresserLauncher.setCurrentPanelDirty(); 
        ExpresserLauncher.repaint();
    }
      });
  }
  if (isReadOnly() || model == null) {
      item.setEnabled(false);
  }
  return item;
    }
    
    protected JMenuItem showValueOnlyMenuItem() {
  final String showValueOnlyLabel =
      MiGenUtilities.getLocalisedMessage("ShowValueOnly");
  final ExpresserModel model = getModel();
  final int displayMode = getTiedNumberExpression().getDisplayMode();
  JMenuItem item = new JMenuItem(showValueOnlyLabel);
  if (displayMode == TiedNumberExpression.DISPLAY_ONLY_VALUE) {
      item.setEnabled(false);
  } else {
      item.addActionListener(new ActionListener() {

    
    public void actionPerformed(ActionEvent e) {
        UIEventManager.processEvent(
          new TiedNumberMenuItemEvent(getIdentityForLogMessage(), showValueOnlyLabel));
//        logActionPerformed(showValueOnlyLabel);
        tiedNumber.setDisplayMode(TiedNumberExpression.DISPLAY_ONLY_VALUE);
        // so property mirrors are updated
        ExpresserLauncher.setCurrentPanelDirty(); 
        // the following works around the problem that if this tied
        // number is also in the task variables panel it needs
        // to be repainted
        // TODO: determine if the following is no longer needed
        ExpresserLauncher.repaint();
    }
      });
  }
  if (isReadOnly() || model == null) {
      item.setEnabled(false);
  }
  return item;
    }
    
    protected JMenuItem editNameMenuItem() {
  final int displayMode = getTiedNumberExpression().getDisplayMode();
  final String editNameLabel =
      MiGenUtilities.getLocalisedMessage("EditName");
  JMenuItem item = new JMenuItem(editNameLabel);
  if (displayMode == TiedNumberExpression.DISPLAY_NAME_AND_VALUE || displayMode == TiedNumberExpression.DISPLAY_ONLY_NAME) {
      item.addActionListener(new ActionListener() {

    
    public void actionPerformed(ActionEvent e) {
        UIEventManager.processEvent(
          new TiedNumberMenuItemEvent(getIdentityForLogMessage(), editNameLabel));
//        logActionPerformed(editNameLabel);
        editName();
    }
      });
  } else {
      item.setEnabled(false);
  }
  return item;
    }
    
    protected JMenuItem removeThisMenuItem() {
  final String removeExpressionLabel =
      MiGenUtilities.getLocalisedMessage("RemoveExpressionFromCanvas");
  JMenuItem item = new JMenuItem(removeExpressionLabel);
  final Container parent = TiedNumberPanel.this.getParent();
  if (!(parent instanceof ExpressionPanel<?>)) {
      System.err.println("Expected this tied number panel to be part of an expression panel.");
      return item;
  }
  final ExpressionPanel<?> expressionPanelParent = (ExpressionPanel<?>) parent;
  item.addActionListener(new ActionListener() {

      @SuppressWarnings({ "unchecked", "rawtypes" })
      public void actionPerformed(ActionEvent e) {
    UIEventManager.processEvent(
      new TiedNumberMenuItemEvent(getIdentityForLogMessage(), removeExpressionLabel));
    Container grandParent = expressionPanelParent.getParent();
    if (expressionPanelParent.isOnACanvas() && expressionPanelParent.okToRemoveExpression()) {
        grandParent.remove(expressionPanelParent);
        grandParent.repaint();
    } else {    
        if (grandParent instanceof ExpressionPanel &&
          ((ExpressionPanel<?>) grandParent).okToRemoveExpression()) {
            ExpressionPanel.removeSubExpression(
              (ExpressionPanel<IntegerValue>) parent, 
              (ExpressionPanel<IntegerValue>) expressionPanelParent.getParent());
        } else {
            expressionPanelParent.setExpression(new UnspecifiedTiedNumberExpression());
            grandParent.validate();
            grandParent.repaint();
        }
    }
    ExpresserLauncher.setCurrentModelDirty();
      }
  });
  return item;
    }
    
    protected JMenuItem copyThisMenuItem() {
  final String copyExpressionLabel =
      MiGenUtilities.getLocalisedMessage("CopyExpression");
  JMenuItem item = new JMenuItem(copyExpressionLabel);
  Container parent = TiedNumberPanel.this.getParent();
  if (!(parent instanceof ExpressionPanel<?>)) {
      System.err.println("Expected this tied number panel to be part of an expression panel.");
      return item;
  }
  final ExpressionPanel<?> expressionPanelParent = (ExpressionPanel<?>) parent;
  if (isOnACanvas()) {
      item.addActionListener(new ActionListener() {

    
    public void actionPerformed(ActionEvent e) {
        UIEventManager.processEvent(
          new TiedNumberMenuItemEvent(getIdentityForLogMessage(), copyExpressionLabel));
        ExpressionPanel<?> copy = expressionPanelParent.createCopy();
        getCanvas().add(copy);
        copy.setLocation(expressionPanelParent.getX() + expressionPanelParent.getWidth() / 2, 
                   expressionPanelParent.getY() + expressionPanelParent.getHeight() / 2);
        copy.setDragCreatesCopy(false);
    }
      });
  } else {
      item.setEnabled(false);
  }
  return item;
    }
    
    protected JMenuItem showContainersMenuItem(
      final boolean considerEverything,
      final boolean searchActivityDocument) {
  final ObjectSetCanvas canvas = findCanvas();
  if (canvas == null) {
      return null;
  }
//  final ObjectSetCanvas slaveCanvas = 
//      ExternalInterface.getSlaveCanvas(ExternalInterface.getTabbedPanelSelectedIndex());
  final ArrayList<ExpresserModel> modelsOfHighlightedProxies = canvas.getModelsOfHighlightedProxies();
  final ArrayList<Timer> timersOfHighlightedTiedNumberPanels = canvas.getTimersOfHighlightedTiedNumberPanels();
  final ExpresserModel model = canvas.getModel();
  boolean alreadyHighlighting = 
      (model != null && !model.getTemporaryHighlightedShapes().isEmpty()) ||
      !modelsOfHighlightedProxies.isEmpty() ||
      !timersOfHighlightedTiedNumberPanels.isEmpty();
  if (alreadyHighlighting && considerEverything) {
      // don't want to copies of the remove highlighting menu item
      return null;
  }
  final boolean constructionExpressionTiedNumber = 
      getTiedNumberExpression().isConstructionExpressionCoefficient();
  String messageName;
  if (alreadyHighlighting) {
      messageName = "RemoveHighlighting"; 
  } else if (considerEverything) {
      messageName = "ShowTiedNumberContainers";
  } else {
      if (constructionExpressionTiedNumber) {
    messageName = "showBBWithThisInConstructionExpression";
      } else {
    messageName = "ShowPatternsRepeatingWithTiedNumber";
      }      
  }
  final String label = MiGenUtilities.getLocalisedMessage(messageName);
  JMenuItem item = new JMenuItem(label);
  if (model == null) { // used to include isReadOnly() || 
      item.setEnabled(false);
  } else {
      ActionListener processEventAction = 
    new ActionListener() {
    
    public void actionPerformed(ActionEvent e) {
        UIEventManager.processEvent(
          new TiedNumberMenuItemEvent(getIdentityForLogMessage(), label));
    }
      };
      item.addActionListener(processEventAction);
      ActionListener action; 
      if (alreadyHighlighting) {
    action =
        new ActionListener() {

        public void actionPerformed(ActionEvent e) {
      canvas.clearHighlighting();
      canvas.repaint();
        }
    };  
      } else {
    action =
        new ActionListener() {

        public void actionPerformed(ActionEvent e) {
      Walker walker = new Walker() {

          @Override
          public boolean tiedNumberFound(
            TiedNumberExpression<?> encounteredTiedNumber,
            BlockShape shape,
            AttributeHandle<IntegerValue> handle,
            ExpresserModel containingModel) {
        if (containingModel == null) {
            return true;
        }
        if (shape == null) {
            return true;
        }
        TiedNumberExpression<?> thisTiedNumber;
        if (containingModel != null && containingModel.isSlaved()) {
            thisTiedNumber = 
          ((ExpresserModelSlaveCopy) containingModel).getSlaveCopy(tiedNumber);
        } else {
            thisTiedNumber = tiedNumber;
        }
        if (thisTiedNumber.getOriginal() == encounteredTiedNumber.getOriginal()) {
            if (constructionExpressionTiedNumber) {
          BlockShape superShape = shape.getSuperShape();
          if (superShape instanceof GroupShape) {
              GroupShape superGroupShape = (GroupShape) superShape;
              if (!superGroupShape.isEntireModel()) {
            List<BlockShape> shapes = superGroupShape.getShapes();
            for (BlockShape subShape : shapes) {
                if (subShape.equivalent(shape)) {
              // equivalent sibling so highlight it
              containingModel.addTemporaryHighlightedShape(subShape);
                }
            }
              }
          } else {
              containingModel.addTemporaryHighlightedShape(shape);
          }
            } else if (considerEverything || handle == PatternShape.ITERATIONS) {
          BlockShape shapeToHighlight;
          if (shape instanceof PatternShape && !considerEverything) {
              PatternShape patternShape = (PatternShape) shape;
              // first shape is adjusted for negative deltas
              // unlike getShape()
              shapeToHighlight = patternShape.getShape(0);
          } else {
              shapeToHighlight = shape;
          }
          if (searchActivityDocument && containingModel != null) {
              containingModel.addTemporaryHighlightedShape(shapeToHighlight);
              if (!modelsOfHighlightedProxies.contains(containingModel)) {
            modelsOfHighlightedProxies.add(containingModel);
              }
          } else {
              containingModel.addTemporaryHighlightedShape(shapeToHighlight);
          }
            }
        }
        return true;
          }
          
          @Override
          public boolean tiedNumberPanelFound(ExpressionPanel<IntegerValue> expressionPanelOfTiedNumber) {      
        if (considerEverything &&
          expressionPanelOfTiedNumber.getTiedNumberExpression().getOriginal() == 
              tiedNumber.getOriginal()) {
            Color originalBackground = expressionPanelOfTiedNumber.getBackground();  
            Timer timer = expressionPanelOfTiedNumber.createHighlightTimer(originalBackground);
            timersOfHighlightedTiedNumberPanels.add(timer);
        }
        return true;        
          }

      };
      ActivityDocument activityDocument = null;
      if (searchActivityDocument) {
          activityDocument = canvas.getActivityDocument();
      }
      if (activityDocument == null) {
          if (canvas.walkToTiedNumbers(walker, true)) {
        BlockShapeCanvasPanel masterPanel = ExpresserLauncher.getSelectedMasterPanel();
        ArrayList<ExpressionPanel<IntegerValue>> globalAllocationExpressionPanels = 
            masterPanel.getGlobalAllocationExpressionPanels();
        if (globalAllocationExpressionPanels != null) {
            for (ExpressionPanel<IntegerValue> expressionPanel : globalAllocationExpressionPanels) {
          expressionPanel.walkToTiedNumbers(walker);
            }
        }
//        if (slaveCanvas != null) {
//            slaveCanvas.walkToTiedNumbers(walker, true);
//        }
          }
      } else {
          activityDocument.walkToTiedNumbers(walker);
          ExpresserLauncher.setCurrentPanelDirty();
      }
      if (modelsOfHighlightedProxies.isEmpty() &&
        timersOfHighlightedTiedNumberPanels.isEmpty() &&
        model.getTemporaryHighlightedShapes().isEmpty()) {
          JOptionPane.showMessageDialog(
            SwingUtilities.getWindowAncestor(TiedNumberPanel.this),
            MiGenUtilities.getLocalisedMessage("NothingFoundToSelect"));
      }
        }
    };
      }
      item.addActionListener(action);
  }
  return item;
    }

    protected void editName() {
  String name = tiedNumber.getName();
  nameEditField = new JTextField(name);
  nameEditField.setFont(nameFont());
  nameEditField.setColumns(Math.max(8, name.length()));
  KeyListener listenForEnter = new KeyListener() {

      public void keyPressed(KeyEvent e) {
    if (e.getKeyChar() == KeyEvent.VK_ENTER) {
        editCompleted();
    }
      }

      public void keyReleased(KeyEvent e) {
      }

      public void keyTyped(KeyEvent e) {
      }
      
  };
  nameEditField.addKeyListener(listenForEnter);
  addNameFocusListener(nameEditField);
  nameFieldPanel.remove(nameLabel);
  nameFieldPanel.add(nameEditField);
  nameEditField.setSize(nameEditField.getPreferredSize());
  nameFieldPanel.setSize(nameFieldPanel.getPreferredSize());
  nameEditField.requestFocus();
  nameEditField.setSelectionStart(0);
  nameEditField.setSelectionEnd(name.length());
//  nameFieldPanel.setComponentZOrder(nameEditField, 0);
  setSize(getPreferredSize());
  updateSizeOfAncestorExpressionPanels();
    }

    protected void updateSizeOfAncestorExpressionPanels() {
  Container parent = getParent();
  if (parent instanceof ExpressionPanel<?>) {
      ((ExpressionPanel<?>) parent).updateSizeOfAncestorExpressionPanels();
  }
    }
    
    protected void editCompleted() {
  if (!editingName()) {
      return;
  }
  String newName = nameEditField.getText();
  if (nameLabel.getText().equals(newName)) {
      return; // no change
  }
  nameFieldPanel.remove(nameEditField);
  nameFieldPanel.add(nameLabel);
  nameEditField = null;
  tiedNumber.setName(newName);
  if (newName.equals(NAME_IF_NO_NAME_GIVEN)) {
//      createDialog("NoNameGiven");
      tiedNumber.setDisplayMode(TiedNumberExpression.DISPLAY_ONLY_VALUE);
      tiedNumber.setNamed(false);
  } else {
      nameLabel.setText(tiedNumber.getName()); // + authorInfo());
      setSize(getPreferredSize());
      ExpresserLauncher.autoSaveCurrentModel();
      TiedNumberExpression<?> otherWithName = isNameInUse(tiedNumber);
      if (otherWithName != null) {
    if (isNameInUse(tiedNumber) != null) {
        createDialog("NameInUse");
        editName();
    } else {
        tiedNumber.setDisplayAuthorInfo(true);
        otherWithName.setDisplayAuthorInfo(true);
    }
      } else if (newName.length() == 0 ||
           !Character.isLetter(newName.charAt(0))) {
    createDialog("NameImproper");
    editName();
      }
  }
  updateSizeOfAncestorExpressionPanels();
    }
    
    public TiedNumberExpression<?> isNameInUse(TiedNumberExpression<?> other) {
  ObjectSetCanvas canvas = getCanvas();
  if (canvas == null) {
      return null;
  }
  return canvas.isNameInUse(other);
    }

    public boolean editingName() {
  return nameEditField != null;
    }

    protected boolean okToEditValue() {
  return !tiedNumber.isReadOnly() && 
         (!readOnly || MiGenConfiguration.isAllowEditingOfSlaveUnlockedNumbers());
    }

    protected ExpresserModel getModel() {
  Container ancestor = getParent();
  while (ancestor != null) {
      if (ancestor instanceof ExpresserModelPanel) {
    return ((ExpresserModelPanel) ancestor).getModel();
      } else if (ancestor instanceof DocumentCanvas) {
    return ((DocumentCanvas) ancestor).getModel();
      }
      ancestor = ancestor.getParent();
  }
  return null;
    }
    
    public TiedNumberPanel<V> createCopy() {  
  return createTiedNumberPanelCopy();
    }

    public TiedNumberPanel<V> createTiedNumberPanelCopy() {
  // if readOnly not specified use its value for this
  return createTiedNumberPanelCopy(this.readOnly);
    }
    
    public TiedNumberPanel<V> createTiedNumberPanelCopy(boolean readOnly) {
  // number itself is shared among all copies
  // that is the "magic" of tied numbers
  return new TiedNumberPanel<V>(tiedNumber.createCopy(), readOnly);
    }

    private void addNameFieldUpdateListener() {
  nameFieldUpdateListener =
      new UpdateListener<TiedNumberExpression<IntegerValue>>() {

      public void objectUpdated(
        UpdateEvent<TiedNumberExpression<IntegerValue>> e) {
    processNameChange();
      }
  };
  tiedNumber.addNameFieldUpdateListener(nameFieldUpdateListener);
    }

    private void addNameFocusListener(final JTextField editField) {
  if (editField == null) {
      // see issue 544 -- probably unspecified -- e.g. a ? value
      // could also be vertical layout
      return; 
  }
  FocusListener focusListener = new FocusListener() {

      public void focusGained(FocusEvent e) {
      }

      public void focusLost(FocusEvent e) {
    Color color = editField.getForeground();
    if (color.equals(NAME_IN_USE_COLOR)) {
        createDialog("NameInUse");
    } else if (color.equals(NAME_IMPROPER_COLOR)) {
        createDialog("NameImproper");
    } else if (color.equals(NAME_NOT_GIVEN_COLOR)) {
        createDialog("NoNameGiven");
    }
    // see Issue 1012
    editCompleted();
      }

  };
  editField.addFocusListener(focusListener);
    }

    public void createDialog(String messageName) {
  String message = MiGenUtilities.getLocalisedMessage(messageName);
  Stencil stencil = MiGenUtilities.getStencil(this);
  ModalPaperNoteCallOut intervention = new ModalPaperNoteCallOut(stencil, message);
  intervention.showMessage();
    }

    protected void addDisplayModeUpdateListener() {
  displayModeUpdateListener =
      new UpdateListener<TiedNumberExpression<IntegerValue>>() {

      public void objectUpdated(
        UpdateEvent<TiedNumberExpression<IntegerValue>> e) {
    displayModeUpdated();
      }
  };
  tiedNumber.addDisplayModeUpdateListener(displayModeUpdateListener);
    }

    protected void addTiedNumberValueUpdateListener() {
  tiedNumber.addUpdateListener(tiedNumberUpdateListener);
    }

    // private void removeTiedNumberValueUpdateListener() {
    // tiedNumber.removeUpdateListener(tiedNumberUpdateListener);
    // }

    protected MasterSlaveUniverseMicroworldPanel getMasterSlaveUniverseMicroworldPanel() {
  Container parent = getParent();
  while (parent != null
    && !(parent instanceof MasterSlaveUniverseMicroworldPanel)) {
      parent = parent.getParent();
  }
  return (MasterSlaveUniverseMicroworldPanel) parent;
    }

    public TiedNumberExpression<IntegerValue> getTiedNumberExpression() {
  return tiedNumber;
    }

    public String getTiedNumberName() {
  // better to get the name from the tiedNumber??
  return nameLabel.getText();
    }

    public void setPlayButtonEnabled(boolean enabled) {
  playButton.setEnabled(enabled);
    }

    protected void displayModeUpdated() {
  if (editingName()) {
      return;
  }
  int displayMode = tiedNumber.getDisplayMode();
  updateBackgroundAppearance();
  Dimension valueDimension = valueTextFieldPanel.getPreferredSize();
  valueTextField.setEditable(!tiedNumber.isLocked() && okToEditValue());
  updateValueTextFieldBackground();
  switch (displayMode) {
  case TiedNumberExpression.DISPLAY_ONLY_VALUE:
      add(valueTextFieldPanel);
      remove(nameFieldPanel);
      setSize(valueDimension.width + HORIZONTAL_GAP,
        valueDimension.height + VERTICAL_GAP);
      setBorderAndFontForValueOnly();
      invalidate();
      break;
  case TiedNumberExpression.DISPLAY_ONLY_NAME:
      remove(valueTextFieldPanel);
      add(nameFieldPanel, BorderLayout.NORTH);
      nameFieldPanel.setSize(nameFieldPanel.getPreferredSize());
      setSize(getPreferredSize());
      break;
  case TiedNumberExpression.DISPLAY_NAME_AND_VALUE:
      setBorderAndFontForNameAndValue(); 
      add(nameFieldPanel, BorderLayout.NORTH);
      nameFieldPanel.setSize(nameFieldPanel.getPreferredSize());
      add(valueTextFieldPanel, BorderLayout.SOUTH);
      valueTextFieldPanel.setSize(valueTextFieldPanel.getPreferredSize());
      setSize(getPreferredSize());
      break;
  }
  updateSize();
  Container parent = getParent();
  if (parent != null) {
      Container grandparent = parent.getParent();
      if (grandparent != null && grandparent instanceof AttributeManifest) {
    Container canvas = grandparent.getParent();
    if (canvas != null && canvas instanceof ObjectSetCanvas) {
        ((ObjectSetCanvas) canvas).setDirtyManifests(true);
    }
      }
  }
  updateSizeOfAncestorExpressionPanels();
    }

    protected boolean isUntiedNumberPanel() {
  // Overridden by UntiedNumberPanel
  return false;
    }

    public void updateBackgroundAppearance() {
  Color currentBackground = getBackground();
  Color newBackground;
  if (getTiedNumberExpression().isLocked()) {
      newBackground = Color.LIGHT_GRAY;
  } else {
      newBackground = Color.PINK;
  }
  if (!currentBackground.equals(newBackground)) {
      changeBackgroundColor(newBackground);
  }
  setBorder(defaultBorder());
    }
    
    public void displayDropFeedback(boolean on, boolean topLevel) {
  if (on) {
      String dropFeedbackColor = MiGenConfiguration.getDropFeedbackColor();
      try {
    changeBackgroundColor(Color.decode(dropFeedbackColor));
      } catch (NumberFormatException e) {
    System.err.println("Unable to parse the color " + dropFeedbackColor);
    e.printStackTrace();
      }
  } else {
      updateBackgroundAppearance();
  }
    }

    public void changeBackgroundColor(Color newBackgroundColor) {
  if (editingName()) {
      return;
  }
  valueTextFieldPanel.setBackground(newBackgroundColor);
  // what would have changed it?
  // TODO: determine if this does anything
  nameFieldPanel.setBackground(NAME_BACKGROUND_COLOR_VERTICAL);
  nameFieldPanel.setBorder(defaultBorder());
  setBackground(newBackgroundColor);
    }

    // following needed to support drag and drop:

    private void enableDragAndDrop() {
  final TransferHandler transferHandler = new TransferHandler() {

      public boolean canImport(JComponent comp, DataFlavor[] drageeDataFlavors) {
    if (TiedNumberPanel.this.canImport()) {
        for (int i = 0; i < drageeDataFlavors.length; i++) {
      if (drageeDataFlavors[i].equals(tiedNumberPanelFlavor)) {
          return true;
      }
        }
    }
    return false;
      }

      public boolean importData(JComponent comp, Transferable t) {
    return TiedNumberPanel.this.canImport();
      }

      public int getSourceActions(JComponent c) {
    return transferMode;
      }

      public Transferable createTransferable(JComponent c) {
    try {
        return TiedNumberPanel.this.getTransferData(tiedNumberPanelFlavor);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
      }
  };
  // initialise transfer handler so the data (i.e. the tied number) can be
  // transfered to the drop object
  setTransferHandler(transferHandler);
  // set up listener to drag events
  dragSource = DragSource.getDefaultDragSource();
  dragGestureRecognizer =
      dragSource.createDefaultDragGestureRecognizer(this, transferMode, this);
  // following needed to see tied number while being dragged
  dragSourceMotionListener =
      new DisplayDragSourceDragSourceMotionListener(tiedNumberPanelFlavor);
  dragSource.addDragSourceMotionListener(dragSourceMotionListener);
    }

    protected boolean canImport() {
  return true;
    }
    
    private void disableDragAndDrop() {
  setTransferHandler(null);
  if (dragGestureRecognizer != null) {
      dragGestureRecognizer.removeDragGestureListener(this);
      dragGestureRecognizer = null;
  }
    }

    protected Transferable createTransferable() {
  try {
      return getTransferData(tiedNumberPanelFlavor);
  } catch (Exception e) {
      e.printStackTrace();
      return null;
  }
    }

    static public DataFlavor createTiedNumberPanelDataFlavor() {
  try {
      return new DataFlavor(
        DataFlavor.javaJVMLocalObjectMimeType
          + ";class=uk.ac.lkl.migen.system.expresser.ui.TiedNumberPanel");
  } catch (Exception e) {
      e.printStackTrace();
      return null;
  }
    }

    public TiedNumberPanel<?> getTransferData(DataFlavor flavor)
      throws UnsupportedFlavorException, IOException {
  return this;
    }

    public DataFlavor[] getTransferDataFlavors() {
  return dataFlavors;
    }

    public boolean isDataFlavorSupported(DataFlavor flavor) {
  return flavor.equals(tiedNumberPanelFlavor);
    }

    @SuppressWarnings("unchecked")
    public void dragGestureRecognized(final DragGestureEvent event) {
  if (ExpressionPanel.isDragStarted()) {
      return;
  }
  ExpressionPanel.setDragStarted(true);
  ObjectSetCanvas canvas = findCanvas();
  ExpresserModel model = canvas==null ? null : canvas.getModel();
  if (model != null && model.isSlaved()) {
      // if slaved then drag a copy of the master equivalent
      canvas = canvas.findMasterCanvas();
      ExpresserModelSlaveCopy slaveModel = (ExpresserModelSlaveCopy) model;
      TiedNumberExpression<IntegerValue> masterEquivalent =
    (TiedNumberExpression<IntegerValue>) 
        slaveModel.getMasterTiedNumber(getTiedNumberExpression());
      dragee = new ExpressionPanel<IntegerValue>(masterEquivalent);
  } else {
      dragee = createDragee();
  }
  dragee.setPanelStartingDrag(this);
  dragee.setDragEnabled(false); // until dropped
  ExpresserLauncher.add(dragee);
  Point location = getLocation();
  location = SwingUtilities.convertPoint(getParent(), location, SwingUtilities.getWindowAncestor(this));
  dragee.setLocation(location);
  UIEventManager.processEvent(new TiedNumberDragStartEvent(dragee.getId().toString(), canvas.getId().toString(), dragee.getId().toString()));
//  dragee.logMessage("Drag started");
  try {
      event.startDrag(DragSource.DefaultCopyDrop, dragee.createTransferable(), dragee);
//  } catch (InvalidDnDOperationException e) {
//      System.out.println("Ignoring drag error.");
//      e.printStackTrace();
  } catch (Exception e) {
      System.out.println("Ignoring drag error.");
      e.printStackTrace();
      ExpressionPanel.setDragStarted(false);
  }
  if (!dragCreatesCopy() && canvas != null) { 
      // added copy; so remove the original to behave like a move
      // postpone this until clear if cross-canvas drag 
      // which copies or within-canvas drag which just moves
      dragee.setPanelStartingDrag(this);
//      canvas.remove(this);
  }
    }

    protected ExpressionPanel<IntegerValue> createDragee() {
  return new ExpressionPanel<IntegerValue>(getTiedNumberExpression().createCopy());
    }
    
    public static boolean isMacOS() {
  String osName = System.getProperty("os.name").toLowerCase();
  return osName.startsWith("mac os x");
    }

    private ObjectSetCanvas findCanvas() {
  // a tied number can be on a canvas directly
  // or in a container that "knows" the canvas
  // Ideally the dragging should happen on a layer above the entire panel
  // but attempts to do some had multiple problems including
  // the tied number panel looking like just a pink box.
  Container ancestor = getParent();
  while (ancestor != null) {
      if (ancestor instanceof ObjectSetCanvas) {
    return (ObjectSetCanvas) ancestor;
      } else if (ancestor instanceof GlobalColorAllocationPanel) {
    GlobalColorAllocationPanel globalColorAllocationPanel =
      (GlobalColorAllocationPanel) ancestor;
    return globalColorAllocationPanel.getCanvas();
      } else if (ancestor instanceof MasterSlaveUniverseMicroworldPanel) {
    MasterSlaveUniverseMicroworldPanel masterSlaveUniverseMicroworldPanel =
      (MasterSlaveUniverseMicroworldPanel) ancestor;
    return masterSlaveUniverseMicroworldPanel.getMasterPanel().getCanvas();
      }
      ancestor = ancestor.getParent();
  }
  return null;
    }

    public void dragDropEnd(DragSourceDropEvent dsde) {
//  logMessage("Drag ended");
   ExpressionPanel.setDragStarted(false);
  // if it was not dropped on something willing to receive it
  // e.g. the slave panel
  ExpresserLauncher.remove(this); 
  // if left on the canvas should not create a copy when dragged
//  boolean onACanvas = onACanvas();
//  setDragCreatesCopy(!onACanvas);
//  if (onACanvas) {
//      setDragEnabled(true);
//  }
//  Container topLevelAncestor = getTopLevelAncestor();
//  if (topLevelAncestor != null) {
//      // see Issue 582
//      topLevelAncestor.repaint();
//  }
  ExpresserLauncher.setCurrentPanelDirty();
    }

    protected ObjectSetCanvas getCanvas() {
  Container ancestor = getParent();
  while (ancestor != null) {
      if (ancestor instanceof ObjectSetCanvas) {
    return (ObjectSetCanvas) ancestor;
      }
      ancestor = ancestor.getParent();
  }
  return null;
    }

    public void dragEnter(DragSourceDragEvent dsde) {
    }

    public void dragExit(DragSourceEvent dse) {
    }

    public void dragOver(DragSourceDragEvent dsde) {
    }

    public void dropActionChanged(DragSourceDragEvent dsde) {
    }

    public static DataFlavor getFlavor() {
  return tiedNumberPanelFlavor;
    }

    public void setDragCreatesCopy(boolean flag) {
  if (flag) {
      transferMode = TransferHandler.COPY;
  } else {
      transferMode = TransferHandler.MOVE;
  }
    }

    public boolean dragCreatesCopy() {
  return transferMode == TransferHandler.COPY;
    }

    public void setDragEnabled(boolean dragEnabled) {
  this.dragEnabled = dragEnabled;
  if (dragEnabled) {
      enableDragAndDrop();
  } else {
      disableDragAndDrop();
  }
    }

    public boolean isDragEnabled() {
  return dragEnabled;
    }

    public void setDropEnabled(boolean dropEnabled) {
  this.dropEnabled = dropEnabled;
  if (dropEnabled) {
      DropTargetListener dropTargetListener = new DropTargetListener() {

    public void dragEnter(DropTargetDragEvent dtde) {
        Transferable transferable = dtde.getTransferable();
        if (transferable.isDataFlavorSupported(TiedNumberPanel.getFlavor()) || 
      transferable.isDataFlavorSupported(ExpressionPanel.getFlavor())) {
      // expression panel is generating drop feedback
      // this caused Issue 902
//      displayDropFeedback(true, true);
      dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
        }
    }

    public void dragExit(DropTargetEvent dte) {
//        TiedNumberPanel.this.displayDropFeedback(false, true);
    }

    public void dragOver(DropTargetDragEvent dtde) {
    }

    @SuppressWarnings("unchecked")
    // this warning is a serious problem if we ever had tied numbers
    // of more than one kind of Value
    // since the drag doesn't preserve the generic information -- no
    // problem if we stick with IntegerValue
    public void drop(DropTargetDropEvent dtde) {
        ExpressionPanel.setDragStarted(false);
        TiedNumberPanel.this.updateBackgroundAppearance();
        Transferable transferable = dtde.getTransferable();
        try {
      Component transferData =
          (Component) transferable.getTransferData(TiedNumberPanel.getFlavor());
      double dropX = dtde.getLocation().getX();
      boolean droppedOnLeftHalf =
          dropX < TiedNumberPanel.this.getWidth() / 2;
      ExpressionPanel.acquireFocusForAncestorAttributeManifest(
        TiedNumberPanel.this);
      if (transferData instanceof TiedNumberPanelProxy) {
          TiedNumberPanelProxy<IntegerValue> tiedNumberPanelProxy =
            (TiedNumberPanelProxy<IntegerValue>) transferData;
          TiedNumberExpression<IntegerValue> droppedExpression =
        tiedNumberPanelProxy.getTiedNumberExpression().getOriginal();
          handleDropOfExpressionOnNumber(
            droppedOnLeftHalf, droppedExpression, tiedNumberPanelProxy);
      } else if (transferData instanceof TiedNumberPanel) {
          TiedNumberPanel<IntegerValue> tiedNumberPanel =
            (TiedNumberPanel<IntegerValue>) transferData;
          if (!tiedNumberPanel.isReadOnly() || 
        TiedNumberPanel.this.isPartOfGlobalColorAllocationPanel()) {
        tiedNumberPanel.setDragCreatesCopy(true);
        TiedNumberExpression<IntegerValue> droppedExpression =
            tiedNumberPanel.getTiedNumberExpression();
        handleDropOfExpressionOnNumber(
          droppedOnLeftHalf, droppedExpression,
          tiedNumberPanel);
          }
      } else if (transferData instanceof ExpressionPanel) {
          ExpressionPanel<IntegerValue> expressionDropTarget =
            (ExpressionPanel<IntegerValue>) transferData;
          if (!expressionDropTarget.isReadOnly() || 
        TiedNumberPanel.this.isPartOfGlobalColorAllocationPanel()) {
        expressionDropTarget.setDragCreatesCopy(true);
        Expression<IntegerValue> droppedExpression =
            expressionDropTarget.getExpression();
        handleDropOfExpressionOnNumber(
          droppedOnLeftHalf, droppedExpression,
          expressionDropTarget);
          }
      }
      dtde.dropComplete(true);
        } catch (Exception e) {
      e.printStackTrace();
      dtde.rejectDrop();
        }
        ExpresserLauncher.repaint();
    }

    public void dropActionChanged(DropTargetDragEvent dtde) {
    }

      };
      addDropTargetListener(dropTargetListener);
  } else {
      setDropTarget(null);
  }
    }

    public void addDropTargetListener(DropTargetListener dropTargetListener) {
  new DropTarget(this, dropTargetListener);
  DropTargetListener forwardingListener =
      forwardToParentDropTargetListener(dropTargetListener);
  new DropTarget(valueTextField, forwardingListener);
  new DropTarget(getNameComponent(), forwardingListener);
    }
    
    public static DropTargetListener forwardToParentDropTargetListener(
      final DropTargetListener parentDropTargetListener) {
  return new DropTargetListener() {

      public void dragEnter(DropTargetDragEvent dtde) {
    parentDropTargetListener.dragEnter(dtde);
      }

      public void dragExit(DropTargetEvent dte) {
    parentDropTargetListener.dragExit(dte);
      }

      public void dragOver(DropTargetDragEvent dtde) {
    parentDropTargetListener.dragOver(dtde);
      }

      public void drop(DropTargetDropEvent dtde) {
    parentDropTargetListener.drop(dtde);
      }

      public void dropActionChanged(DropTargetDragEvent dtde) {
    parentDropTargetListener.dropActionChanged(dtde);
      }

  };
    }

    
    protected void handleDropOfExpressionOnNumber(
      final boolean droppedOnLeftHalf,
      final Expression<IntegerValue> droppedExpression,
      final JComponent droppedPanel) {
  // I believe this is now obsolete -- see Issue 653
  UIEventManager.processEvent(new TiedNumberMenuPopupEvent(getIdentityForLogMessage(), "Drop"));
  JPopupMenu menu = new JPopupMenu();
  JMenuItem item;
  final String replaceLabel =
      MiGenUtilities.getLocalisedMessage("ReplaceTiedNumberLabel");
  item = new JMenuItem(replaceLabel);
  item.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
    UIEventManager.processEvent(
      new TiedNumberMenuItemEvent(getIdentityForLogMessage(), replaceLabel));
//    logActionPerformed(replaceLabel);
    TiedNumberExpression<IntegerValue> tiedNumber = getTiedNumberExpression();
    tiedNumber.setValue(droppedExpression.evaluate());
    ExpressionPanel.removeComponent(droppedPanel);
      }

  });
  menu.add(item);
  final String addLabel =
      MiGenUtilities.getLocalisedMessage("AddToTiedNumberLabel");
  item = new JMenuItem(addLabel);
  item.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
    UIEventManager.processEvent(
      new TiedNumberMenuItemEvent(getIdentityForLogMessage(), 
                            addLabel + " " + (droppedOnLeftHalf ? "(left)" : "(right)")));
//    logActionPerformed(addLabel + " "
//                 + (droppedOnLeftHalf ? "(left)" : "(right)"));
    replaceWithOperation(droppedOnLeftHalf, droppedExpression,
                   new IntegerAdditionOperator(), droppedPanel);
    // IntegerValue value =
    // tiedNumberPanel.getTiedNumberExpression().getValue();
    // IntegerValue newValue =
    // getTiedNumberExpression().getValue().add(value);
    // getTiedNumberExpression().setValue(newValue);
    // ExpressionEditorDropTarget.removeDroppedComponent(
    // tiedNumberPanel);
      }

  });
  menu.add(item);
  final String subtractLabel =
      droppedOnLeftHalf ?
      MiGenUtilities.getLocalisedMessage("SubtractFromTiedNumberLabelLeft") :
    MiGenUtilities.getLocalisedMessage("SubtractFromTiedNumberLabelRight");
  item = new JMenuItem(subtractLabel);
  item.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
    UIEventManager.processEvent(
      new TiedNumberMenuItemEvent(getIdentityForLogMessage(), 
                            subtractLabel + " " + (droppedOnLeftHalf ? "(left)" : "(right)")));
//    logActionPerformed(subtractLabel + " "
//      + (droppedOnLeftHalf ? "(left)" : "(right)"));
    replaceWithOperation(droppedOnLeftHalf, droppedExpression,
      new IntegerSubtractionOperator(), droppedPanel);
    // TiedNumberExpression<IntegerValue> otherTiedNumber =
    // tiedNumberPanel.getTiedNumberExpression();
    // IntegerValue myValue = getTiedNumberExpression().getValue();
    // IntegerValue otherValue = otherTiedNumber.getValue();
    // IntegerValue newValue;
    // if (droppedOnLeftHalf) {
    // newValue = otherValue.subtract(myValue);
    // } else {
    // newValue = myValue.subtract(otherValue);
    // }
    // getTiedNumberExpression().setValue(newValue);
    // ExpressionEditorDropTarget.removeDroppedComponent(
    // tiedNumberPanel);
      }

  });
  menu.add(item);
  final String multiplyLabel =
      MiGenUtilities.getLocalisedMessage("MultiplyTiedNumberLabel");
  item = new JMenuItem(multiplyLabel);
  item.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
    UIEventManager.processEvent(
      new TiedNumberMenuItemEvent(getIdentityForLogMessage(), 
                            multiplyLabel + " " + (droppedOnLeftHalf ? "(left)" : "(right)")));
//    logActionPerformed(multiplyLabel + " "
//      + (droppedOnLeftHalf ? "(left)" : "(right)"));
    replaceWithOperation(droppedOnLeftHalf, droppedExpression,
      new IntegerMultiplicationOperator(), droppedPanel);
    // IntegerValue value =
    // droppedTiedNumberPanel.getTiedNumberExpression().getValue();
    // IntegerValue newValue =
    // getTiedNumberExpression().getValue().multiply(value);
    // getTiedNumberExpression().setValue(newValue);
    // ExpressionEditorDropTarget.removeDroppedComponent(
    // droppedTiedNumberPanel);
      }

  });
  menu.add(item);
  menu.show(this, 0, getHeight());
    }

    private void replaceWithOperation(boolean droppedOnLeftHalf,
      Expression<IntegerValue> droppedExpression,
      Operator<IntegerValue, IntegerValue> operator,
      JComponent droppedPanel) {
  ExpressionList<IntegerValue> operands;
  if (droppedOnLeftHalf) {
      operands =
    new ExpressionList<IntegerValue>(droppedExpression,
                               getTiedNumberExpression());
  } else {
      operands =
    new ExpressionList<IntegerValue>(getTiedNumberExpression(),
                               droppedExpression);
  }
  ModifiableOperation<IntegerValue, IntegerValue> operation =
      new ModifiableOperation<IntegerValue, IntegerValue>(operator, operands);
  setReplacement(operation);
  Container parent = getParent();
  Point location = this.getLocation();
  ExpressionPanel<IntegerValue> expressionPanel =
      new ExpressionPanel<IntegerValue>(operation);
  expressionPanel.setDragCreatesCopy(false);
  expressionPanel.setLocation(location);
//  if (parent instanceof ObjectSetCanvas) {
//      ObjectSetCanvas canvas = (ObjectSetCanvas) parent;
//      ExpresserModel model = canvas.getModel();
//      model.addLocatedExpression(expressionPanel.getLocatedExpression());
//      model.removeLocatedExpression(getLocatedExpression());
//  }
  parent.add(expressionPanel);
  parent.remove(this);
  ExpressionPanel.removeComponent(droppedPanel);
  parent.repaint();
    }

    public boolean isDropEnabled() {
  return dropEnabled;
    }

    public Expression<IntegerValue> getReplacement() {
  return replacement;
    }

    public void setReplacement(Expression<IntegerValue> replacement) {
  this.replacement = replacement;
  fireObjectUpdated();
    }

    public void fireObjectUpdated() {
  updateSupport.fireObjectUpdated();
    }

    public void addUpdateListener(UpdateListener<TiedNumberPanel<V>> listener) {
  updateSupport.addListener(listener);
    }

    public void removeUpdateListener(UpdateListener<TiedNumberPanel<V>> listener) {
  updateSupport.removeListener(listener);
    }

    public boolean canAcceptEditedValue() {
  try {
      String text = valueTextField.getText();
      Integer.parseInt(text);
      return true;
  } catch (NumberFormatException event) {
      return false;
  }
    }

    public boolean acceptEditedValue() {
  try {
      String text = valueTextField.getText();
      int value = Integer.parseInt(text);
      getTiedNumberExpression().setValue(new IntegerValue(value));
      valueTextField.setForeground(Color.BLACK);
      if (!readOnly) {
    updateSize();
      }
      return true;
  } catch (NumberFormatException event) {
      // just leave things alone since might be typing a negative number
      // or the like
      valueTextField.setForeground(Color.RED);
      return false;
  }
    }

    private void processNameChange() {
  String newName = tiedNumber.getName();
  if (nameLabel != null) {
      nameLabel.setText(newName);
  }
  if (nameFieldPanel == null) {
      return; // can happen with unspecified tied number panels
  }
  if (newName.equals(NAME_IF_NO_NAME_GIVEN)) {
      if (!nameFieldPanel.getForeground().equals(NAME_NOT_GIVEN_COLOR)) {
    UIEventManager.processEvent(
      new TiedNumberColorEvent(getIdentityForLogMessage(),  "Name color changed to 'name not given color'"));
//    logMessage("Name color changed to 'name not given color'");
    nameFieldPanel.setForeground(NAME_NOT_GIVEN_COLOR);
      }
//      nameFieldPanel.requestFocus();
//      nameFieldPanel.setSelectionStart(0);
//      nameFieldPanel.setSelectionEnd(4);
  } else if (newName.length() > 0
       && Character.isLetter(newName.charAt(0))) {
      ObjectSetCanvas canvas = getCanvas();
      if (canvas != null && canvas.isNameInUse(tiedNumber) != null) {
    if (!nameFieldPanel.getForeground().equals(NAME_IN_USE_COLOR)) {
      UIEventManager.processEvent(
        new TiedNumberColorEvent(getIdentityForLogMessage(), "Name color changed to 'name in use color'"));
//        logMessage("Name color changed to 'name in use color'");
        nameFieldPanel.setForeground(NAME_IN_USE_COLOR);
    }
      } else {
    Color nameOkColor = nameOkColor();
    if (!nameFieldPanel.getForeground().equals(nameOkColor)) {
        UIEventManager.processEvent(
          new TiedNumberColorEvent(getIdentityForLogMessage(), "Name color changed to 'name OK color'"));
//        logMessage("Name color changed to 'name OK color'");
        nameFieldPanel.setForeground(nameOkColor);
    }
      }
  } else {
      if (!nameFieldPanel.getForeground().equals(NAME_IMPROPER_COLOR)) {
    UIEventManager.processEvent(
      new TiedNumberColorEvent(getIdentityForLogMessage(), "Name color changed to 'improper color'"));
//    logMessage("Name color changed to 'improper color'");
    nameFieldPanel.setForeground(NAME_IMPROPER_COLOR);
      }
  }
//  nameFieldPanel.setText(newName);
  if (!readOnly) {
      updateSize();
  }
  ExpresserLauncher.autoSaveCurrentModel();
    }

    protected Color nameOkColor() {
  return NAME_OK_COLOR_VERTICAL;
    }
    
    protected Font nameFont() {
  return getNameFontVertical();
    }
    
    protected Font getNameFontVertical() {
  if (NAME_FONT_VERTICAL == null) {
      NAME_FONT_VERTICAL = 
    new Font(MiGenConfiguration.getNumberNameFontFamily(),
       MiGenConfiguration.isNumberNameFontBold() ? Font.BOLD: Font.PLAIN,
       MiGenConfiguration.getNumberNameFontSize());
  }
  return NAME_FONT_VERTICAL;
    }
    
    protected Font getValueFont() {
  if (VALUE_FONT == null) {
      VALUE_FONT = 
    new Font(MiGenConfiguration.getNumberValueFontFamily(),
       MiGenConfiguration.isNumberValueFontBold() ? Font.BOLD: Font.PLAIN,
       MiGenConfiguration.getNumberValueFontSize());
  }
  return VALUE_FONT;
    }
    
    protected Border defaultBorder() {
  return DEFAULT_BORDER_VERTICAL;
    }

    public void updateSize() {
  // following ensures that the size of the panel and its components
  // will be maintained to the same values that fresh copies have
  Component nameComponent = getNameComponent();
  Dimension namePreferredSize = nameComponent.getPreferredSize();
  nameComponent.setSize(namePreferredSize);
  Dimension valuePreferredSize = valueTextField.getPreferredSize();
  valueTextField.setSize(valuePreferredSize);
  valueTextFieldPanel.setSize(valueTextFieldPanel.getPreferredSize());
  setSize(getPreferredSize());
  updateSizeOfAncestorExpressionPanels();
  invalidate();
  ExpresserLauncher.repaint();
    }

    public boolean isReadOnly() {
  return readOnly;
    }

    protected boolean isPartOfGlobalColorAllocationPanel() {
  Container parent = getParent();
  Container ancestor = parent;
  while (ancestor != null) {
      if (ancestor instanceof GlobalColorAllocationPanel) {
    return true;
      }
      ancestor = ancestor.getParent();
  }
  return false;
    }

    public Point getSlaveLocation() {
  if (slaveLocation == null) {
      return getLocation();
  } else {
      return slaveLocation;
  }
    }

    public void setSlaveLocation(Point slaveLocation) {
  this.slaveLocation = slaveLocation;
    }

//    protected void logMenuPoppedUp(String kind) {
//  logAction(kind + " menu popped up");
//    }

//    void logActionPerformed(String menuLabel) {
//  logAction("Clicked '" + menuLabel + "'");
//    }

    public String getIdentityForLogMessage() {
  if (containedInMasterPanel == null) {
      ExpresserModelPanel selectedModelPanel = 
    ExpresserLauncher.getModelCopyTabbedPanel().getSelectedModelPanel();
      if (selectedModelPanel instanceof MasterSlaveUniverseMicroworldPanel) {
    BlockShapeCanvasPanel masterPanel = 
        ((MasterSlaveUniverseMicroworldPanel) selectedModelPanel).getMasterPanel();
    containedInMasterPanel = masterPanel.isAncestorOf(this);
      } else {
    containedInMasterPanel = false;
      }
  }
  if (containedInMasterPanel) {
      return logIdentity();
  }
  return getId().toString();
    }

    @Override
    public void removeNotify() {
  super.removeNotify();
  if (ExpresserLauncher.isRemovalFinal(getParent())) {
      removeListeners();
      disableDragAndDrop();
  }
    }

    public void removeListeners() {
  if (dragSource != null) {
      dragSource.removeDragSourceMotionListener(dragSourceMotionListener);
  }
  tiedNumber.removeNameFieldUpdateListener(nameFieldUpdateListener);
  tiedNumber.removeUpdateListener(tiedNumberUpdateListener);
  tiedNumber.removeDisplayModeUpdateListener(displayModeUpdateListener);
  tiedNumber.removeLockStatusUpdateListener(lockStatusUpdateListener);
    }

    public String logIdentity() {
  return " (" + getId() + " TiedNumber" + tiedNumber.getIdString()
    + " value: " + tiedNumber.toString() + " name: "
    + tiedNumber.getName() + ")";
    }

    public ID getId() {
  return id;
    }

    public JTextField getValueTextField() {
  return valueTextField;
    }

    public ExpressionPanel<IntegerValue> getDragee() {
        return dragee;
    }
    
    public Component getNameComponent() {
  return nameLabel;
    }

    public TiedNumberPanel<V> getNumberPanelStartingDrag() {
        return numberPanelStartingDrag;
    }

    public void setNumberPanelStartingDrag(
      TiedNumberPanel<V> numberPanelStartingDrag) {
        this.numberPanelStartingDrag = numberPanelStartingDrag;
    }
    
    public boolean isOnACanvas() {
  Container parent = getParent();
  return parent != null && 
         parent instanceof ExpressionPanel<?> && 
         ((ExpressionPanel<?>) parent).isOnACanvas();
    }
    
    public LocatedExpression<IntegerValue> getLocatedExpression() {
  return new LocatedExpression<IntegerValue>(tiedNumber, getX(), getY());
//  if (locatedExpression == null) {
//      locatedExpression = new LocatedExpression<IntegerValue>(tiedNumber, getX(), getY());
//  }
//  return locatedExpression;
    }

    public static String hexFormat(Color color) {
        return "#" + Integer.toHexString(color.getRGB() & 0x00ffffff);
    }
    
//    @Override
//    public void setLocation(int x, int y) {
//  super.setLocation(x, y);
//  getLocatedExpression().setX(x);
//  getLocatedExpression().setY(y);
//    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.