/*
* OpPanel.java
* (FScape)
*
* Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved.
*
* This software is published under the GNU General Public License v3+
*
*
* For further information, please contact Hanns Holger Rutz at
* contact@sciss.de
*/
package de.sciss.fscape.gui;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.SyncFailedException;
import java.rmi.NotBoundException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Vector;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import de.sciss.fscape.Application;
import de.sciss.fscape.op.Operator;
import de.sciss.fscape.op.SlotAlreadyConnectedException;
import de.sciss.fscape.spect.SpectStreamSlot;
import de.sciss.fscape.util.Slots;
import de.sciss.gui.GUIUtil;
/**
* GUI panel hosting the spectral
* operators and allowing the user
* to drag them around and wire them together.
*/
public class OpPanel
extends JPanel
implements ClipboardOwner, ActionListener, MouseListener, MouseMotionListener {
// -------- public --------
public static final String OBJ_NAME = "OpPanel";
// -------- private --------
private SpectPatchDlg win;
private DragContext dragContext = null;
// for optimal layout
protected int preferredWidth = 250;
protected int preferredHeight = 150;
// Popup-Menus, die ueber dem Panel bzw. einem Op aufspringen
private PopupStrip popPanel;
private PopupStrip popIcon;
private PopupStrip popCon;
private PopupStrip popNew;
private Component popSource;
private int popX, popY;
// Panel
private static final String MI_NEW = "New";
private static final String MI_PASTE = "Paste";
// Icon
private static final String MI_EDIT = "Edit...";
private static final String MI_RENAME = "Rename...";
private static final String MI_MAKEALIAS = "Make alias";
private static final String MI_CUT = "Cut";
private static final String MI_COPY = "Copy";
private static final String MI_DUPL = "Duplicate";
private static final String MI_REMOVE = "Remove";
// Connector
private static final String MI_ORIGIN = "Origin";
private static final String MI_TARGET = "Target";
private static final String MI_HIDE = "Hide";
private static final String mPanel[][] = { null, { null }, { MI_PASTE }};
private static final String mIcon[][] = {{ MI_EDIT }, { MI_RENAME }, { MI_MAKEALIAS },
{ null }, { MI_CUT }, { MI_COPY }, { MI_DUPL },
{ MI_REMOVE }};
private static final String mCon[][] = { null, null, { null }, { MI_HIDE }};
private static String mNew[][];
private Vector vIcon; // alle OpIcons
private Hashtable hCon; // keys = (writer) slots; values = OpConnector
protected String[] optionsAliasDel = { "Cancel", "Remove", "Transform" };
protected String[] optionsAliasEdit = { "Cancel", "Original", "Transform" };
// -------- public --------
// public Operator addOperator( Operator op );
// public void removeOperator( Operator op );
// public void renameOperator( Operator op, String name );
// public void moveOperator( Operator op, int x, int y );
// public void updateOperator( Operator op );
// public OpIcon getOpIconAt( int x, int y );
public OpPanel(SpectPatchDlg win) {
super();
Map ops;
Iterator opNames;
vIcon = new Vector();
hCon = new Hashtable();
setLayout(null);
addMouseListener( this );
addMouseMotionListener( this );
this.win = win;
// Popup-Menus
ops = Operator.getOperators();
opNames = ops.keySet().iterator();
mPanel[ 0 ] = new String[ 1 + ops.size() ];
mNew = new String[ ops.size() ][ 1 ];
mPanel[ 0 ][ 0 ] = MI_NEW;
for( int i = 1; opNames.hasNext(); i++ ) {
mPanel[ 0 ][ i ] = (String) opNames.next();
mNew[ i-1 ][ 0 ] = mPanel[ 0 ][ i ];
}
popPanel = new PopupStrip( mPanel, this );
popIcon = new PopupStrip( mIcon, this );
popNew = new PopupStrip( mNew, this );
setOpaque( false );
// ImageIcon icon = new ImageIcon( "images/tools.gif", "test" );
}
/**
* @return returns OBJ_NAME so you can identify it ('==' Operator)
*/
public String toString()
{
return OBJ_NAME;
}
public void clear() {
vIcon = new Vector();
hCon = new Hashtable();
removeAll();
}
/**
* Operator bzw. sein Icon auf das Panel legen
* (invokes Document.addOperator)
*/
public Operator addOperator( Operator op, int x, int y )
{
OpIcon icon = (OpIcon) op.getIcon();
synchronized( vIcon ) {
vIcon.addElement( icon );
}
icon.addMouseListener( this );
icon.getLabel().addMouseListener( this );
icon.addMouseMotionListener( this );
icon.setLocation( x, y );
icon.addTo( this );
validateSize( icon );
win.getDoc().addOperator( op ); // setModified() wird aufgerufen
return op;
}
/**
* Operator bzw. sein Icon vom Panel entfernen
* (invokes Document.addOperator)
*/
public void removeOperator( Operator op )
{
OpIcon icon = (OpIcon) op.getIcon();
Enumeration slots = op.getSlots( Slots.SLOTS_LINKED ).elements();
SpectStreamSlot slot1;
SpectStreamSlot slot2;
OpConnector con;
synchronized( vIcon ) {
vIcon.removeElement( icon );
}
while( slots.hasMoreElements() ) {
slot1 = (SpectStreamSlot) slots.nextElement();
slot2 = slot1.getLinked();
con = getConnector( slot1 );
synchronized( hCon ) {
hCon.remove( slot1 );
hCon.remove( slot2 );
}
con.drawArrow( false );
remove( con );
try {
slot1.divorce();
}
catch( NotBoundException e ) {} // was solls...
}
icon.removeFrom( this );
win.getDoc().removeOperator( op ); // setModified() wird aufgerufen
}
/**
* Operator umbenennen
*/
public void renameOperator( Operator op, String name )
{
OpIcon icon = (OpIcon) op.getIcon();
icon.setName( name );
// win.getDoc().setModified( true );
}
/**
* Operator verschieben
*/
public void moveOperator( Operator op, int x, int y )
{
OpIcon icon = (OpIcon) op.getIcon();
Enumeration slots = op.getSlots( Slots.SLOTS_LINKED ).elements();
SpectStreamSlot slot;
OpConnector con;
icon.setLocation( x, y ); // Icon verschieben
while( slots.hasMoreElements() ) {
slot = (SpectStreamSlot) slots.nextElement();
con = getConnector( slot );
con.drawArrow( false );
con.adjustLocation();
// con.drawArrow( true );
validateSize( con );
}
validateSize( icon );
// win.getDoc().setModified( true );
}
/**
* OpConnector verschieben
*/
public void moveConnector(OpConnector con, int x, int y) {
con.drawArrow(false);
con.setLocation(x, y);
con.drawArrow(true);
validateSize(con);
// win.getDoc().setModified( true );
}
/**
* Connector zu einem Slot ermitteln
*/
public OpConnector getConnector( SpectStreamSlot slot )
{
OpConnector con;
synchronized( hCon ) {
con = (OpConnector) hCon.get( slot );
if( con == null ) {
con = (OpConnector) hCon.get( slot.getLinked() );
}
}
return con;
}
/**
* Operatoren verknuepfen
*
* @param slot1 Slot des ersten Operators
* @param slot2 Slot des zweiten Operators
*/
public void linkOperators( SpectStreamSlot slot1, SpectStreamSlot slot2 )
throws NoSuchElementException,
SyncFailedException,
SlotAlreadyConnectedException
{
SpectStreamSlot foo;
OpConnector con;
// make sure slot1 is the writer
if( (slot1.getFlags() & Slots.SLOTS_TYPEMASK) != Slots.SLOTS_WRITER ) {
foo = slot1;
slot1 = slot2;
slot2 = foo;
}
slot1.linkTo( slot2 );
con = new OpConnector( slot1 );
con.addMouseListener( this );
con.addMouseMotionListener( this );
synchronized (hCon) {
hCon.put(slot1, con);
}
add(con);
validateSize(con);
con.drawArrow(true);
// win.getDoc().setModified( true );
}
/**
* Operatoren trennen
*
* @param slot1 Slot des ersten Operators
*/
public void divorceOperators( SpectStreamSlot slot1 )
throws NotBoundException
{
SpectStreamSlot slot2 = slot1.getLinked();
OpConnector con;
slot1.divorce();
con = getConnector( slot1 );
synchronized( hCon ) {
hCon.remove( slot1 );
hCon.remove( slot2 );
}
con.drawArrow( false );
remove( con );
// win.getDoc().setModified( true );
}
/**
* Veraenderungen am Operator mitteilen
*/
public void updateOperator( Operator op )
{
// win.getDoc().setModified( true );
}
/**
* Liefert das OpIcon, das an einem Punkt des Containers liegt
*
* @return null, wenn kein OpIcon dort
*/
public OpIcon getOpIconAt( int x, int y )
{
Component c = getComponentAt( x, y );
if( (c != null) && (c != this) ) { // ignore myself
if(c.toString().equals(OpIcon.OBJ_NAME)) {
return (OpIcon) c;
}
if(c.toString().equals(OpIconLabel.OBJ_NAME)) {
return ((OpIconLabel) c).getOpIcon();
}
}
return null;
}
/**
* Liefert das OpIcon, das sich mit der Referenz ueberschneiden wuerde
*
* @param src OpIcon, das als Referenz dient und "uebersehen" werden soll
* @param hGap horizontaler Mindestabstand; ggf. 0
* @param vGap vertikaler Mindestabstand; ggf. 0
* @return null, wenn kein OpIcon dort
*/
public OpIcon getOpIconAround( int x, int y, OpIcon src, int hGap, int vGap )
{
OpIconLabel srcLab = src.getLabel();
Rectangle icon = src.getBounds();
Rectangle lab = srcLab.getBounds();
Rectangle union = src.getUnionBounds();
int dx = x - (icon.x + (icon.width >> 1));
int dy = y - (icon.y + (icon.height >> 1));
icon.translate( dx - hGap, dy - vGap );
lab.translate( dx - hGap, dy - vGap);
union.translate( dx - hGap, dy - vGap );
icon.width += hGap << 1;
lab.width += hGap << 1;
union.width += hGap << 1;
icon.height += vGap << 1;
lab.height += vGap << 1;
union.height += vGap << 1;
Component cs[] = getComponents();
Component c;
Rectangle dest;
for( int i = 0; i < cs.length; i++ ) {
c = cs[ i ];
if( (c == src) || (c == srcLab) ) continue;
dest = c.getBounds();
if( (dest.x >= union.x + union.width) ||
(dest.y >= union.y + union.height) ||
(dest.x <= union.x - dest.width) ||
(dest.y <= union.y - dest.height) ) continue; // grobe Abschaetzung
if( ((dest.x >= icon.x + icon.width) ||
(dest.y >= icon.y + icon.height) ||
(dest.x <= icon.x - dest.width) ||
(dest.y <= icon.y - dest.height)) &&
((dest.x >= lab.x + lab.width) ||
(dest.y >= lab.y + lab.height) ||
(dest.x <= lab.x - dest.width) ||
(dest.y <= lab.y - dest.height)) ) continue; // genaue Berechnung
if (c.toString().equals(OpIcon.OBJ_NAME)) {
return (OpIcon) c;
}
if (c.toString().equals(OpIconLabel.OBJ_NAME)) {
return ((OpIconLabel) c).getOpIcon();
}
}
return null;
}
// public void update( Graphics g )
// {
// Dimension d = getSize();
// g.clearRect( 0, 0, d.width, d.height );
// paint( g );
// }
public void paintComponent(Graphics g) {
super.paintComponent(g);
// g.setColor(Color.red);
// g.fillRect(0, 0, getWidth(), getHeight());
Enumeration cons;
OpConnector con;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING , RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE );
synchronized (hCon) {
cons = hCon.elements();
while (cons.hasMoreElements()) {
// System.out.println("DRAW");
con = (OpConnector) cons.nextElement();
// con.drawArrow(true);
con.drawArrowCorrect(g2);
}
}
}
/*
public void paint( Graphics g )
{
paintComponent( g );
paintBorder( g );
// super.paint( g );
Enumeration cons;
OpConnector con;
synchronized( hCon ) {
cons = hCon.elements();
while( cons.hasMoreElements() ) {
con = (OpConnector) cons.nextElement();
con.drawArrow( true );
}
}
paintChildren( g );
}
*/
// public boolean isFocusTraversable()
// {
// return false;
// }
public Dimension getPreferredSize()
{
return new Dimension( preferredWidth, preferredHeight );
}
// -------- ClipboardOwner methods --------
public void lostOwnership( Clipboard clipBoard, Transferable contents )
{
Operator op;
try {
op = (Operator) contents.getTransferData( Operator.flavor );
op.dispose();
}
catch( Exception e1 ) {
GUIUtil.displayError( win.getComponent(), e1, "lostOwnership" );
}
}
// -------- Action Listener methods (only Popup-Menus!) --------
public void actionPerformed( ActionEvent e )
{
// PromptDlg pDlg;
String action = e.getActionCommand();
Map ops;
String opName;
Operator op, op2;
Operator newOp = null; // duplicate, make alias, new op ...
OpIcon opIcon;
Transferable clip;
Enumeration aliases;
// ConfirmDlg confirm;
SpectStreamSlot slot1, slot2, slot3;
OpConnector con;
Point loc;
Rectangle bounds;
Dimension dim;
int i, j;
if( !isEnabled() ) return; // not while running the operators
if (action.equals(MI_EDIT)) {
mouseClicked(new MouseEvent(popSource, MouseEvent.MOUSE_CLICKED, 0, // Doppelklick
InputEvent.BUTTON1_MASK, 0, 0, 2, false));
} else if (action.equals(MI_RENAME)) { //-------- Rename --------
opName = JOptionPane.showInputDialog(win.getComponent(), "Rename Operator", popSource.getName());
if( opName != null ) {
renameOperator(((OpIcon) popSource).getOperator(), opName);
}
} else if (action.equals(MI_CUT)) { //-------- Cut --------
op = (Operator) ((OpIcon) popSource).getOperator().clone();
if( op != null ) {
Application.clipboard.setContents(op, this);
removeOperator(((OpIcon) popSource).getOperator());
}
} else if (action.equals(MI_COPY)) { //-------- Copy --------
op = (Operator) ((OpIcon) popSource).getOperator().clone();
if( op != null ) {
Application.clipboard.setContents(op, this);
}
} else if (action.equals(MI_DUPL)) { //-------- Duplicate --------
newOp = (Operator) ((OpIcon) popSource).getOperator().clone();
if( newOp != null ) {
opIcon = (OpIcon) newOp.getIcon();
opName = opIcon.getName();
i = opName.lastIndexOf( " copy" );
if( i >= 0 ) {
try {
if( i + 5 == opName.length() ) {
j = 2;
} else {
j = Integer.parseInt( opName.substring( i + 6 )) + 1;
}
opIcon.setName( opName.substring( 0, i + 5 ) + ' ' + j );
}
catch( NumberFormatException e1 ) {
opIcon.setName( opName + " copy" );
}
} else {
opIcon.setName( opName + " copy" );
}
}
} else if (action.equals(MI_MAKEALIAS)) { //-------- Make Alias --------
op = ((OpIcon) popSource).getOperator();
newOp = (Operator) op.clone();
if( newOp != null ) {
try {
newOp.turnIntoAlias( op );
opIcon = (OpIcon) newOp.getIcon();
opIcon.setName( opIcon.getName() + " alias" );
}
catch( SyncFailedException e1 ) {
newOp.dispose();
newOp = null;
}
catch( SlotAlreadyConnectedException e2 ) {
// ist ok, weil schon vom clone() ein Alias erzeugt wurde
}
}
} else if (action.equals(MI_REMOVE)) { //-------- Remove --------
op = ((OpIcon) popSource).getOperator();
aliases = op.getAliases();
if( aliases.hasMoreElements() ) {
i = JOptionPane.showOptionDialog( win.getComponent(), "You have made aliases from this Operator.\n" +
"Do you want to remove them or shall they\n" +
"be transformed to genuine objects?", "Request",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
optionsAliasDel, optionsAliasDel[2] );
switch( i ) {
case 2:
while( aliases.hasMoreElements() ) {
op2 = (Operator) aliases.nextElement();
op2.turnIntoGenuine();
opIcon = (OpIcon) op2.getIcon();
opName = opIcon.getName();
if( opName.endsWith( " alias" )) {
opIcon.setName( opName.substring( 0, opName.length() - 6 ));
}
aliases = op.getAliases();
}
break;
case 1:
while( aliases.hasMoreElements() ) {
op2 = (Operator) aliases.nextElement();
removeOperator( op2 );
aliases = op.getAliases(); // !java bug: old Enum is obsolete after remove()
}
break;
default: // Cancel
return;
}
}
removeOperator(op);
} else if (action.equals(MI_PASTE)) { //-------- Paste --------
clip = Application.clipboard.getContents(this);
if( clip != null ) {
try {
newOp = (Operator) clip.getTransferData( Operator.flavor );
newOp = (Operator) newOp.clone();
}
catch( Exception e1 ) {
GUIUtil.displayError( win.getComponent(), e1, action );
}
}
} else if(action.equals(MI_HIDE)) {
popSource.dispatchEvent( new MouseEvent( popSource, MouseEvent.MOUSE_CLICKED,
System.currentTimeMillis(), InputEvent.ALT_MASK,
0, 0, 1, false ));
} else { //-------- unknown ---
if(popSource.toString().equals(OpPanel.OBJ_NAME)) { // "new"
ops = Operator.getOperators();
opName = (String) ops.get( action );
if( opName != null ) {
try {
newOp = (Operator) Class.forName( Operator.PACKAGE + "." +
opName ).newInstance();
}
catch( Exception e1 ) {
GUIUtil.displayError( win.getComponent(), e1, action );
}
}
} else if(popSource.toString().equals(OpConnector.OBJ_NAME)) { // change slot
bounds = popSource.getBounds();
try {
slot1 = ((OpConnector) popSource).getOrigin();
slot2 = slot1.getLinked();
divorceOperators( slot1 );
slot3 = (SpectStreamSlot) slot1.getOwner().getSlot( action );
con = null;
if( slot3 != null ) {
linkOperators( slot3, slot2 );
} else {
slot3 = (SpectStreamSlot) slot2.getOwner().getSlot( action );
if( slot3 != null ) {
linkOperators( slot1, slot3 );
}
}
// relocate
if( slot3 != null ) {
con = getConnector( slot3 );
if( con != null ) {
dim = con.getSize();
moveConnector( con, bounds.x + ((bounds.width - dim.width) >> 1),
bounds.y + ((bounds.height - dim.height) >> 1));
}
}
}
catch( NotBoundException e2 ) {
// whole lotta shakin
}
catch( SlotAlreadyConnectedException e3 ) {
// whole lottaxception
}
catch( SyncFailedException e4 ) {
// ignore
}
catch( NoSuchElementException e5 ) {
// ignore
}
}
}
if( newOp != null ) { // neuer Operator angelegt, den fuegen wir ein:
// GUISupport.sendComponentAsleep( this );
opIcon = (OpIcon) newOp.getIcon();
loc = findFreePlaceAround( popX, popY, opIcon );
addOperator( newOp, loc.x - (OpIcon.ICON_WIDTH>>1),
loc.y - (OpIcon.ICON_HEIGHT>>1) );
// GUISupport.wakeComponent( this );
opIcon.requestFocus();
}
}
// -------- Mouse Listener methods --------
public void mouseClicked(MouseEvent e) {
if (!isEnabled() || !e.isPopupTrigger()) return;
String name = e.getSource().toString();
if (name.equals(OpPanel.OBJ_NAME)) {
panelContextMenu(e);
} else if (name.equals(OpIcon.OBJ_NAME)) {
iconContextMenu(e);
} else if (name.equals(OpConnector.OBJ_NAME)) {
connectorContextMenu(e);
}
}
public void mousePressed( MouseEvent e )
{
if( !isEnabled() ) return; // not while running the operators
String name = e.getSource().toString();
if (name.equals(OpPanel.OBJ_NAME)) {
panelPressed(e);
} else if (name.equals(OpIcon.OBJ_NAME)) {
iconPressed(e);
} else if (name.equals(OpConnector.OBJ_NAME)) {
connectorPressed(e);
} else if (name.equals(OpIconLabel.OBJ_NAME)) {
labelPressed(e);
}
}
public void mouseReleased( MouseEvent e )
{
if( !isEnabled() ) return; // not while running the operators
String name = e.getSource().toString();
if (name.equals(OpPanel.OBJ_NAME)) {
panelReleased(e);
} else if (name.equals(OpIcon.OBJ_NAME)) {
iconReleased(e);
} else if (name.equals(OpConnector.OBJ_NAME)) {
connectorReleased(e);
} else if (name.equals(OpIconLabel.OBJ_NAME)) {
labelReleased(e);
}
if( dragContext != null ) {
dragContext.mouseReleased( e );
dragContext = null;
}
}
public void mouseEntered( MouseEvent e ) {}
public void mouseExited( MouseEvent e ) {}
// -------- MouseMotion Listener methods --------
public void mouseDragged( MouseEvent e )
{
if( !isEnabled() || (dragContext == null) ) return;
dragContext.mouseDragged( e );
}
public void mouseMoved( MouseEvent e ) {}
// -------- listens to Panel --------
public void panelPressed( MouseEvent e )
{
SpectStreamSlot slot;
if( e.isAltDown() ) { // Link loeschen
slot = getLinkAround( e.getX(), e.getY(), true );
if( slot != null ) {
try {
divorceOperators( slot );
// neuen Link ziehen
// dragSource = (OpIcon) slot.getOwner().getIcon();
// dragLastX = e.getX();
// dragLastY = e.getY();
// dragType = DRAG_LINK;
mouseDragged( e ); // sofort den Pfeil zeichnen
} catch( NotBoundException e1 ) {
GUIUtil.displayError( win.getComponent(), e1, "panelPressed" );
}
}
} else {
requestFocus();
if( e.isPopupTrigger() ) { // PopUp-Menu
panelContextMenu(e);
}
}
}
private void panelContextMenu(MouseEvent e) {
popSource = this;
popX = e.getX();
popY = e.getY();
this.add( popPanel );
popPanel.show( this, e.getX(), e.getY() );
this.remove( popPanel );
}
public void panelReleased( MouseEvent e )
{
SpectStreamSlot slot;
OpConnector con;
Dimension dim;
if( (e.getClickCount() == 2) && !e.isAltDown() ) {
slot = getLinkAround( e.getX(), e.getY(), true );
if( slot == null ) { // kein Link; dann Popup-Menu
popSource = this;
popX = e.getX();
popY = e.getY();
this.add(popNew);
popNew.show(this, e.getX(), e.getY());
this.remove(popNew);
} else { // Link ==> evtl. Connector sichtbar machen
con = getConnector( slot );
// if( (con != null) && !con.isVisible() ) {
if( con != null ) {
con.drawArrow(false);
dim = con.getSize();
con.setLocation(e.getX() - (dim.width >> 1), e.getY() - (dim.height >> 1));
con.setVisible(true);
// con.drawArrow( true );
updateOperator( slot.getOwner() );
}
}
} else if (dragContext != null) {
dragContext.mouseReleased(e);
dragContext = null;
}
repaint();
}
// -------- listens to Icon --------
private void iconContextMenu(MouseEvent e) {
OpIcon opIcon = (OpIcon) e.getSource();
Point opLoc = opIcon.getLocation();
popSource = opIcon;
popX = e.getX() + opLoc.x;
popY = e.getY() + opLoc.y;
this.add( popIcon );
popIcon.show( popSource, e.getX(), e.getY() );
this.remove( popIcon );
}
public void iconPressed(MouseEvent e) {
if( e.isPopupTrigger() ) { // PopUp-Menu
iconContextMenu(e);
} else if (e.isAltDown()) {
OpIcon opIcon = (OpIcon) e.getSource();
if (!opIcon.getOperator().getSlots(Slots.SLOTS_WRITER | Slots.SLOTS_FREE).isEmpty()) {
dragContext = new DragContext(e, DragContext.LINK);
}
} else {
dragContext = new DragContext(e, DragContext.MOVE);
}
}
public void iconReleased( MouseEvent e )
{
Operator op1, op2;
SpectStreamSlot slot1, slot2;
OpIcon dragSource;
Rectangle srcBounds;
EditOpDlg opDlg;
OpIcon opIcon = (OpIcon) e.getSource();;
// ConfirmDlg confirm;
String opName;
int i;
if( e.getClickCount() == 2 ) {
op1 = opIcon.getOperator();
op2 = op1.getOriginal();
if( op2 != null ) {
i = JOptionPane.showOptionDialog( win.getComponent(), "Aliases cannot be edited.\n" +
"Do you want to edit the original Operator\n" +
"or shall this Alias be transformed\n" +
"into a genuine object?", "Request",
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
optionsAliasEdit, optionsAliasEdit[2] );
switch( i ) {
case 2:
op1.turnIntoGenuine();
opIcon = (OpIcon) op1.getIcon();
opName = opIcon.getName();
if( opName.endsWith( " alias" )) {
opIcon.setName( opName.substring( 0, opName.length() - 6 ));
}
break;
case 1:
op1 = op2;
break;
default: // Cancel
return;
}
}
// GUISupport.sendComponentAsleep( win );
opDlg = new EditOpDlg( win.getComponent(), op1 );
opDlg.setVisible( true );
if( opDlg.getChoice() ) {
updateOperator( op1 );
}
opDlg.dispose();
// GUISupport.wakeComponent( win );
} else {
if( dragContext == null ) return;
if( dragContext.hasDragStarted() ) {
dragContext.mouseReleased( e );
if( dragContext.wasDragSuccessful() ) {
dragSource = (OpIcon) e.getComponent();
srcBounds = dragSource.getBounds();
switch( dragContext.getType() ) {
case DragContext.MOVE: //-------- Moved the Icon -------------------
moveOperator( dragSource.getOperator(),
srcBounds.x + e.getX() - (srcBounds.width >> 1),
srcBounds.y + e.getY() - (srcBounds.height >> 1));
break;
case DragContext.LINK: //-------- Drew an Arrow -------------------
op1 = dragSource.getOperator();
try {
// for now just take the first one
slot1 = (SpectStreamSlot) op1.getSlots( Slots.SLOTS_WRITER |
Slots.SLOTS_FREE ).firstElement();
op2 = ((OpIcon) dragContext.getTarget()).getOperator();
slot2 = (SpectStreamSlot) op2.getSlots( Slots.SLOTS_READER |
Slots.SLOTS_FREE ).firstElement();
linkOperators( slot1, slot2 );
}
catch( Exception e1 ) {
GUIUtil.displayError( win.getComponent(), e1, "link" );
}
break;
default:
break;
}
}
} else {
dragContext.mouseReleased( e );
}
dragContext = null;
}
repaint();
}
// -------- listens to Connector --------
private void connectorContextMenu(MouseEvent e) {
OpConnector opCon = (OpConnector) e.getSource();
Point conLoc = opCon.getLocation();
Vector slots;
String mName;
popSource = opCon;
popX = e.getX() + conLoc.x;
popY = e.getY() + conLoc.y;
// construct menu
slots = opCon.getOrigin().getOwner().getSlots(
Slots.SLOTS_FREE | Slots.SLOTS_WRITER);
mName = MI_ORIGIN;
for (int i = 0; i < 2; i++) {
if (!slots.isEmpty()) {
mCon[i] = new String[1 + slots.size()];
mCon[i][0] = mName;
for (int j = 0; j < slots.size(); j++) {
mCon[i][j + 1] = slots.elementAt(j).toString();
}
} else {
mCon[i] = new String[0]; // will not be displayed
}
slots = opCon.getOrigin().getLinked().getOwner().getSlots(
Slots.SLOTS_FREE | Slots.SLOTS_READER);
mName = MI_TARGET;
}
popCon = new PopupStrip(mCon, this);
this.add(popCon);
popCon.show(popSource, e.getX(), e.getY());
this.remove(popCon);
}
public void connectorPressed(MouseEvent e) {
if (e.isPopupTrigger()) { // PopUp-Menu
connectorContextMenu(e);
} else { // prepare Drag
dragContext = new DragContext(e, DragContext.MOVE);
}
}
public void connectorReleased(MouseEvent e) {
OpConnector dragSource;
Rectangle srcBounds;
OpConnector con = (OpConnector) e.getComponent();
if (e.isAltDown()) { // hide connector
con.drawArrow (false);
con.setVisible(false);
// con.drawArrow (true );
updateOperator(con.getOrigin().getOwner());
} else {
if (dragContext == null) return;
if (dragContext.hasDragStarted()) {
dragContext.mouseReleased(e);
if (dragContext.wasDragSuccessful()) {
dragSource = (OpConnector) e.getComponent();
srcBounds = dragSource.getBounds();
moveConnector(dragSource,
srcBounds.x + e.getX() - (srcBounds.width >> 1),
srcBounds.y + e.getY() - (srcBounds.height >> 1));
}
} else {
dragContext.mouseReleased(e);
}
dragContext = null;
}
repaint();
}
// -------- listens to IconJLabel --------
public void labelPressed( MouseEvent e ) {}
public void labelReleased( MouseEvent e )
{
if( e.getClickCount() == 2 ) {
popSource = ((OpIconLabel) e.getComponent()).getOpIcon();
actionPerformed( new ActionEvent( this, ActionEvent.ACTION_PERFORMED,
MI_RENAME ));
}
}
// -------- private methods --------
/*
* Vergroessert die Panelflaeche, wenn eine Componente
* ueber dessen Dimensionen hinausragt
*/
protected void validateSize( Component c )
{
Dimension dim = getSize();
Rectangle bnd;
Container parent;
if( c.toString() == OpIcon.OBJ_NAME ) {
bnd = ((OpIcon) c).getUnionBounds();
} else {
bnd = c.getBounds();
}
if( !contains( bnd.x + bnd.width + 3, bnd.y + bnd.height + 3 )) {
preferredWidth = Math.max( dim.width, bnd.x + bnd.width + 3 );
preferredHeight = Math.max( dim.height, bnd.y + bnd.height + 3 );
parent = getParent();
parent.doLayout();
}
}
/*
* Sucht nach einem freien Ort fuer ein OpIcon
*
* @param x Ausgangs-Punkt, X-Koordinate
* @param y Ausgangs-Punkt, Y-Koordinate
* @param src OpIcon, fuer das Platz gesucht werden soll
* @return Mittelpunkt fuer das neue Icon
*/
private Point findFreePlaceAround( int x, int y, OpIcon src )
{
double maxArc = 2.0 * Math.PI;
double arc = maxArc;
double r = 0.0;
int cx, cy;
do {
cx = x + ((int) (r * Math.cos( arc )));
cy = y + ((int) (r * Math.sin( arc )));
if( getOpIconAround( cx, cy, src, 4, 4 ) == null ) {
// keine Punkte zu weit links/oben
if( (cx >= (OpIcon.ICON_WIDTH>>1)) && (cy >= (OpIcon.ICON_HEIGHT>>1)) ) {
return new Point( cx, cy );
}
}
if( arc >= maxArc ) { // naechster konzentrischer Kreis mit groesserem Radius
arc = 0.0;
r += 6.0;
} else { // Kreis ziehen mit konstanter Strecke
arc += 6.0 / r;
}
} while( true ); // irgendwann kommen wir unten/rechts an einen freien Punkt
}
/*
* Liefert den Link, der in der Naehe eines Punktes des Containers liegt
*
* @param origin true, wenn der Writer-Slot geliefert werden soll
* (also der Pfeilanfang), false fuer den Reader (Pfeilende)
* @return null, wenn kein Link in der Naehe
*/
private SpectStreamSlot getLinkAround( int x, int y, boolean origin )
{
Enumeration cons;
OpConnector con;
int dist = 5;
int newDist;
SpectStreamSlot nearSlot = null;
synchronized( hCon ) {
cons = hCon.elements();
while( cons.hasMoreElements() ) {
con = (OpConnector) cons.nextElement();
newDist = OpConnector.getDistance( con, x, y );
if( (newDist >= 0) && (newDist < dist) ) {
nearSlot = con.getOrigin();
}
}
}
if( nearSlot != null ) {
if( (nearSlot.getFlags() & (origin ? Slots.SLOTS_WRITER : Slots.SLOTS_READER )) == 0 ) {
nearSlot = nearSlot.getLinked();
}
}
return nearSlot;
}
}
// class OpPanel