weka.gui.knowledgeflow.LayoutPanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of weka-dev Show documentation
Show all versions of weka-dev Show documentation
The Waikato Environment for Knowledge Analysis (WEKA), a machine
learning workbench. This version represents the developer version, the
"bleeding edge" of development, you could say. New functionality gets added
to this version.
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* LayoutPanel.java
* Copyright (C) 2015 University of Waikato, Hamilton, New Zealand
*
*/
package weka.gui.knowledgeflow;
import weka.core.*;
import weka.core.converters.FileSourcedConverter;
import weka.gui.Perspective;
import weka.gui.knowledgeflow.VisibleLayout.LayoutOperation;
import weka.gui.visualize.PrintablePanel;
import weka.knowledgeflow.KFDefaults;
import weka.knowledgeflow.StepManager;
import weka.knowledgeflow.StepManagerImpl;
import weka.knowledgeflow.steps.Loader;
import weka.knowledgeflow.steps.Note;
import javax.swing.*;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Menu;
import java.awt.MenuItem;
import java.awt.Point;
import java.awt.PopupMenu;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Provides a panel just for laying out a Knowledge Flow graph. Also listens for
* mouse events for editing the flow.
*
* @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
* @version $Revision: $
*/
public class LayoutPanel extends PrintablePanel {
/** For serialization */
private static final long serialVersionUID = 4988098224376217099L;
/** Grid spacing */
protected int m_gridSpacing;
/** The flow contained in this LayoutPanel as a visible (graphical) flow */
protected VisibleLayout m_visLayout;
protected int m_currentX;
protected int m_currentY;
protected int m_oldX;
protected int m_oldY;
/** Thread for loading data for perspectives */
protected Thread m_perspectiveDataLoadThread;
/**
* Constructor
*
* @param vis the {@code VisibleLayout} to display
*/
public LayoutPanel(VisibleLayout vis) {
super();
m_visLayout = vis;
setLayout(null);
setupMouseListener();
setupMouseMotionListener();
m_gridSpacing =
m_visLayout.getMainPerspective().getSetting(KFDefaults.GRID_SPACING_KEY,
KFDefaults.GRID_SPACING);
}
/**
* Configure mouse listener
*/
protected void setupMouseListener() {
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent me) {
LayoutPanel.this.requestFocusInWindow();
double z = m_visLayout.getZoomSetting() / 100.0;
double px = me.getX();
double py = me.getY();
py /= z;
px /= z;
if (m_visLayout.getMainPerspective().getPalleteSelectedStep() == null) {
if (((me.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK)
&& m_visLayout.getFlowLayoutOperation() == VisibleLayout.LayoutOperation.NONE) {
StepVisual step =
m_visLayout.findStep(new Point((int) px, (int) py));
if (step != null) {
m_visLayout.setEditStep(step);
m_oldX = (int) px;
m_oldY = (int) py;
m_visLayout.setFlowLayoutOperation(LayoutOperation.MOVING);
}
if (m_visLayout.getFlowLayoutOperation() != LayoutOperation.MOVING) {
m_visLayout.setFlowLayoutOperation(LayoutOperation.SELECTING);
m_oldX = (int) px;
m_oldY = (int) py;
m_currentX = m_oldX;
m_currentY = m_oldY;
Graphics2D gx = (Graphics2D) LayoutPanel.this.getGraphics();
gx.setXORMode(java.awt.Color.white);
gx.dispose();
}
}
}
}
@Override
public void mouseReleased(MouseEvent me) {
LayoutPanel.this.requestFocusInWindow();
if (m_visLayout.getEditStep() != null
&& m_visLayout.getFlowLayoutOperation() == LayoutOperation.MOVING) {
if (m_visLayout.getMainPerspective().getSnapToGrid()) {
int x = snapToGrid(m_visLayout.getEditStep().getX());
int y = snapToGrid(m_visLayout.getEditStep().getY());
m_visLayout.getEditStep().setX(x);
m_visLayout.getEditStep().setY(y);
snapSelectedToGrid();
}
m_visLayout.setEditStep(null);
revalidate();
repaint();
m_visLayout.setFlowLayoutOperation(LayoutOperation.NONE);
}
if (m_visLayout.getFlowLayoutOperation() == LayoutOperation.SELECTING) {
revalidate();
repaint();
m_visLayout.setFlowLayoutOperation(LayoutOperation.NONE);
double z = m_visLayout.getZoomSetting() / 100.0;
double px = me.getX();
double py = me.getY();
py /= z;
px /= z;
highlightSubFlow(m_currentX, m_currentY, (int) px, (int) py);
}
}
@Override
public void mouseClicked(MouseEvent me) {
LayoutPanel.this.requestFocusInWindow();
Point p = me.getPoint();
Point np = new Point();
double z = m_visLayout.getZoomSetting() / 100.0;
double px = me.getX();
double py = me.getY();
px /= z;
py /= z;
np.setLocation(p.getX() / z, p.getY() / z);
StepVisual step = m_visLayout.findStep(np);
if (m_visLayout.getFlowLayoutOperation() == LayoutOperation.ADDING
|| m_visLayout.getFlowLayoutOperation() == LayoutOperation.NONE) {
// try and popup a context sensitive menu if we've been
// clicked over a step
if (step != null) {
if (me.getClickCount() == 2) {
if (!step.getStepManager().isStepBusy()
&& !m_visLayout.isExecuting()) {
popupStepEditorDialog(step);
}
} else if ((me.getModifiers() & InputEvent.BUTTON1_MASK) != InputEvent.BUTTON1_MASK
|| me.isAltDown()) {
stepContextualMenu(step, (int) (p.getX() / z),
(int) (p.getY() / z));
return;
} else {
// just select this step
List v = m_visLayout.getSelectedSteps();
if (!me.isShiftDown()) {
v = new ArrayList();
}
v.add(step);
m_visLayout.setSelectedSteps(v);
return;
}
} else {
if ((me.getModifiers() & InputEvent.BUTTON1_MASK) != InputEvent.BUTTON1_MASK
|| me.isAltDown()) {
if (!m_visLayout.isExecuting()) {
canvasContextualMenu((int) px, (int) py);
revalidate();
repaint();
m_visLayout.getMainPerspective().notifyIsDirty();
}
return;
} else if (m_visLayout.getMainPerspective()
.getPalleteSelectedStep() != null) {
// if there is a user-selected step from the design palette then
// add the step
// snap to grid
double x = px;
double y = py;
if (m_visLayout.getMainPerspective().getSnapToGrid()) {
x = snapToGrid((int) x);
y = snapToGrid((int) y);
}
m_visLayout.addUndoPoint();
m_visLayout.addStep(m_visLayout.getMainPerspective()
.getPalleteSelectedStep(), (int) x, (int) y);
m_visLayout.getMainPerspective().clearDesignPaletteSelection();
m_visLayout.getMainPerspective().setPalleteSelectedStep(null);
m_visLayout.setFlowLayoutOperation(LayoutOperation.NONE);
m_visLayout.setEdited(true);
}
}
revalidate();
repaint();
m_visLayout.getMainPerspective().notifyIsDirty();
}
if (m_visLayout.getFlowLayoutOperation() == LayoutOperation.PASTING
&& m_visLayout.getMainPerspective().getPasteBuffer().length() > 0) {
try {
m_visLayout.pasteFromClipboard((int) px, (int) py);
} catch (WekaException e) {
m_visLayout.getMainPerspective().showErrorDialog(e);
}
m_visLayout.setFlowLayoutOperation(LayoutOperation.NONE);
m_visLayout.getMainPerspective().setCursor(
Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
return;
}
if (m_visLayout.getFlowLayoutOperation() == LayoutOperation.CONNECTING) {
// turn off connector points and remove connecting line
repaint();
for (StepVisual v : m_visLayout.getRenderGraph()) {
v.setDisplayConnectors(false);
}
if (step != null
&& step.getStepManager() != m_visLayout.getEditStep()
.getStepManager()) {
// connection is valid because only valid connections will
// have appeared in the contextual popup
m_visLayout.addUndoPoint();
m_visLayout.connectSteps(
m_visLayout.getEditStep().getStepManager(),
step.getStepManager(), m_visLayout.getEditConnection());
m_visLayout.setEdited(true);
repaint();
}
m_visLayout.setFlowLayoutOperation(LayoutOperation.NONE);
m_visLayout.setEditStep(null);
m_visLayout.setEditConnection(null);
}
if (m_visLayout.getSelectedSteps().size() > 0) {
m_visLayout.setSelectedSteps(new ArrayList());
}
}
});
}
/**
* Configure mouse motion listener
*/
protected void setupMouseMotionListener() {
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent me) {
double z = m_visLayout.getZoomSetting() / 100.0;
double px = me.getX();
double py = me.getY();
px /= z;
py /= z;
if (m_visLayout.getEditStep() != null
&& m_visLayout.getFlowLayoutOperation() == LayoutOperation.MOVING) {
int deltaX = (int) px - m_oldX;
int deltaY = (int) py - m_oldY;
m_visLayout.getEditStep().setX(
m_visLayout.getEditStep().getX() + deltaX);
m_visLayout.getEditStep().setY(
m_visLayout.getEditStep().getY() + deltaY);
for (StepVisual v : m_visLayout.getSelectedSteps()) {
if (v != m_visLayout.getEditStep()) {
v.setX(v.getX() + deltaX);
v.setY(v.getY() + deltaY);
}
}
repaint();
m_oldX = (int) px;
m_oldY = (int) py;
m_visLayout.setEdited(true);
}
if (m_visLayout.getFlowLayoutOperation() == LayoutOperation.SELECTING) {
repaint();
m_oldX = (int) px;
m_oldY = (int) py;
}
}
@Override
public void mouseMoved(MouseEvent me) {
double z = m_visLayout.getZoomSetting() / 100.0;
double px = me.getX();
double py = me.getY();
px /= z;
py /= z;
if (m_visLayout.getFlowLayoutOperation() == LayoutOperation.CONNECTING) {
repaint();
m_oldX = (int) px;
m_oldY = (int) py;
}
}
});
}
@Override
public void paintComponent(Graphics gx) {
Color backG =
m_visLayout.getMainPerspective().getSetting(KFDefaults.LAYOUT_COLOR_KEY,
KFDefaults.LAYOUT_COLOR);
if (!backG.equals(getBackground())) {
setBackground(backG);
}
double lz = m_visLayout.getZoomSetting() / 100.0;
((Graphics2D) gx).scale(lz, lz);
if (m_visLayout.getZoomSetting() < 100) {
((Graphics2D) gx).setStroke(new BasicStroke(2));
}
super.paintComponent(gx);
((Graphics2D) gx).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) gx).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
paintStepLabels(gx);
paintConnections(gx);
if (m_visLayout.getFlowLayoutOperation() == LayoutOperation.CONNECTING) {
gx.drawLine(m_currentX, m_currentY, m_oldX, m_oldY);
} else if (m_visLayout.getFlowLayoutOperation() == LayoutOperation.SELECTING) {
gx.drawRect(m_currentX < m_oldX ? m_currentX : m_oldX,
m_currentY < m_oldY ? m_currentY : m_oldY,
Math.abs(m_oldX - m_currentX), Math.abs(m_oldY - m_currentY));
}
if (m_visLayout.getMainPerspective().getSetting(KFDefaults.SHOW_GRID_KEY,
KFDefaults.SHOW_GRID)) {
Color gridColor =
m_visLayout.getMainPerspective().getSetting(KFDefaults.GRID_COLOR_KEY,
KFDefaults.GRID_COLOR);
gx.setColor(gridColor);
int gridSpacing =
m_visLayout.getMainPerspective().getSetting(
KFDefaults.GRID_SPACING_KEY, KFDefaults.GRID_SPACING);
int layoutWidth =
m_visLayout.getMainPerspective().getSetting(
KFDefaults.LAYOUT_WIDTH_KEY, KFDefaults.LAYOUT_WIDTH);
int layoutHeight =
m_visLayout.getMainPerspective().getSetting(
KFDefaults.LAYOUT_HEIGHT_KEY, KFDefaults.LAYOUT_HEIGHT);
Stroke original = ((Graphics2D) gx).getStroke();
Stroke dashed =
new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0,
new float[] { 3 }, 0);
((Graphics2D) gx).setStroke(dashed);
for (int i = gridSpacing; i < layoutWidth / lz; i += gridSpacing) {
gx.drawLine(i, 0, i, (int) (layoutHeight / lz));
}
for (int i = gridSpacing; i < layoutHeight / lz; i += gridSpacing) {
gx.drawLine(0, i, (int) (layoutWidth / lz), i);
}
((Graphics2D) gx).setStroke(original);
}
}
@Override
public void doLayout() {
super.doLayout();
for (StepVisual v : m_visLayout.getRenderGraph()) {
Dimension d = v.getPreferredSize();
v.setBounds(v.getX(), v.getY(), d.width, d.height);
v.revalidate();
}
}
/**
* Render the labels for each step in the layout
*
* @param gx the graphics context to use
*/
protected void paintStepLabels(Graphics gx) {
gx.setFont(new Font(null, Font.PLAIN, m_visLayout.getMainPerspective()
.getSetting(KFDefaults.STEP_LABEL_FONT_SIZE_KEY,
KFDefaults.STEP_LABEL_FONT_SIZE)));
FontMetrics fm = gx.getFontMetrics();
int hf = fm.getAscent();
for (StepVisual v : m_visLayout.getRenderGraph()) {
if (!v.getDisplayStepLabel()) {
continue;
}
int cx = v.getX();
int cy = v.getY();
int width = v.getWidth();
int height = v.getHeight();
String label = v.getStepName();
int labelwidth = fm.stringWidth(label);
if (labelwidth < width) {
gx.drawString(label, (cx + (width / 2)) - (labelwidth / 2), cy + height
+ hf + 2);
} else {
// split label
// find mid point
int mid = label.length() / 2;
// look for split point closest to the mid
int closest = label.length();
int closestI = -1;
for (int z = 0; z < label.length(); z++) {
if (label.charAt(z) < 'a') {
if (Math.abs(mid - z) < closest) {
closest = Math.abs(mid - z);
closestI = z;
}
}
}
if (closestI != -1) {
String left = label.substring(0, closestI);
String right = label.substring(closestI, label.length());
if (left.length() > 1 && right.length() > 1) {
gx.drawString(left,
(cx + (width / 2)) - (fm.stringWidth(left) / 2), cy + height
+ (hf * 1) + 2);
gx.drawString(right, (cx + (width / 2))
- (fm.stringWidth(right) / 2), cy + height + (hf * 2) + 2);
} else {
gx.drawString(label, (cx + (width / 2))
- (fm.stringWidth(label) / 2), cy + height + (hf * 1) + 2);
}
} else {
gx.drawString(label,
(cx + (width / 2)) - (fm.stringWidth(label) / 2), cy + height
+ (hf * 1) + 2);
}
}
}
}
/**
* Render the connections between steps
*
* @param gx the graphics object to use
*/
protected void paintConnections(Graphics gx) {
for (StepVisual stepVis : m_visLayout.getRenderGraph()) {
Map> outConns =
stepVis.getStepManager().getOutgoingConnections();
if (outConns.size() > 0) {
List generatableOutputConnections =
stepVis.getStepManager().getStepOutgoingConnectionTypes();
// iterate over the outgoing connections and paint
// with color according to what is present in
// generatableOutputConnections
int count = 0;
for (Entry> e : outConns.entrySet()) {
String connName = e.getKey();
List connectedSteps = e.getValue();
if (connectedSteps.size() > 0) {
int sX = stepVis.getX();
int sY = stepVis.getY();
int sWidth = stepVis.getWidth();
int sHeight = stepVis.getHeight();
for (StepManager target : connectedSteps) {
StepManagerImpl targetI = (StepManagerImpl) target;
int tX = targetI.getStepVisual().getX();
int tY = targetI.getStepVisual().getY();
int tWidth = targetI.getStepVisual().getWidth();
int tHeight = targetI.getStepVisual().getHeight();
Point bestSourcePoint =
stepVis.getClosestConnectorPoint(new Point(tX + (tWidth / 2),
tY + (tHeight / 2)));
Point bestTargetPoint =
targetI.getStepVisual().getClosestConnectorPoint(
new Point(sX + (sWidth / 2), sY + (sHeight / 2)));
gx.setColor(Color.red);
boolean active =
generatableOutputConnections == null
|| !generatableOutputConnections.contains(connName) ? false
: true;
if (!active) {
gx.setColor(Color.gray);
}
gx.drawLine((int) bestSourcePoint.getX(),
(int) bestSourcePoint.getY(), (int) bestTargetPoint.getX(),
(int) bestTargetPoint.getY());
// paint an arrow head
double angle;
try {
double a =
(bestSourcePoint.getY() - bestTargetPoint.getY())
/ (bestSourcePoint.getX() - bestTargetPoint.getX());
angle = Math.atan(a);
} catch (Exception ex) {
angle = Math.PI / 2;
}
Point arrowstart =
new Point(bestTargetPoint.x, bestTargetPoint.y);
Point arrowoffset =
new Point((int) (7 * Math.cos(angle)),
(int) (7 * Math.sin(angle)));
Point arrowend;
if (bestSourcePoint.getX() >= bestTargetPoint.getX()) {
arrowend =
new Point(arrowstart.x + arrowoffset.x, arrowstart.y
+ arrowoffset.y);
} else {
arrowend =
new Point(arrowstart.x - arrowoffset.x, arrowstart.y
- arrowoffset.y);
}
int xs[] =
{ arrowstart.x,
arrowend.x + (int) (7 * Math.cos(angle + (Math.PI / 2))),
arrowend.x + (int) (7 * Math.cos(angle - (Math.PI / 2))) };
int ys[] =
{ arrowstart.y,
arrowend.y + (int) (7 * Math.sin(angle + (Math.PI / 2))),
arrowend.y + (int) (7 * Math.sin(angle - (Math.PI / 2))) };
gx.fillPolygon(xs, ys, 3);
// paint the connection name
int midx = (int) bestSourcePoint.getX();
midx +=
(int) ((bestTargetPoint.getX() - bestSourcePoint.getX()) / 2);
int midy = (int) bestSourcePoint.getY();
midy +=
(int) ((bestTargetPoint.getY() - bestSourcePoint.getY()) / 2) - 2;
gx.setColor((active) ? Color.blue : Color.gray);
// check to see if there is more than one connection
// between the source and target
if (m_visLayout.previousConn(outConns, targetI, count)) {
midy -= 15;
}
gx.drawString(connName, midx, midy);
}
}
count++;
}
}
}
}
/**
* Popup a contextual menu on the canvas that provides options for cutting,
* pasting, deleting selected steps etc.
*
* @param x the x coordinate to pop up at
* @param y the y coordinate to pop up at
*/
protected void canvasContextualMenu(final int x, final int y) {
Map> closestConnections =
m_visLayout.findClosestConnections(new Point(x, y), 10);
PopupMenu contextualMenu = new PopupMenu();
int menuItemCount = 0;
if (m_visLayout.getSelectedSteps().size() > 0) {
MenuItem snapItem = new MenuItem("Snap selected to grid");
snapItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
snapSelectedToGrid();
}
});
contextualMenu.add(snapItem);
menuItemCount++;
MenuItem copyItem = new MenuItem("Copy selected");
copyItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
m_visLayout.copySelectedStepsToClipboard();
m_visLayout.setSelectedSteps(new ArrayList());
} catch (WekaException ex) {
m_visLayout.getMainPerspective().showErrorDialog(ex);
}
}
});
contextualMenu.add(copyItem);
menuItemCount++;
MenuItem cutItem = new MenuItem("Cut selected");
cutItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
m_visLayout.copySelectedStepsToClipboard();
m_visLayout.removeSelectedSteps();
} catch (WekaException ex) {
m_visLayout.getMainPerspective().showErrorDialog(ex);
}
}
});
contextualMenu.add(cutItem);
menuItemCount++;
MenuItem deleteSelected = new MenuItem("Delete selected");
deleteSelected.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
m_visLayout.removeSelectedSteps();
} catch (WekaException ex) {
m_visLayout.getMainPerspective().showErrorDialog(ex);
}
}
});
contextualMenu.add(deleteSelected);
menuItemCount++;
}
if (m_visLayout.getMainPerspective().getPasteBuffer() != null
&& m_visLayout.getMainPerspective().getPasteBuffer().length() > 0) {
contextualMenu.addSeparator();
menuItemCount++;
MenuItem pasteItem = new MenuItem("Paste");
pasteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
m_visLayout.pasteFromClipboard(x, y);
} catch (WekaException ex) {
m_visLayout.getMainPerspective().showErrorDialog(ex);
}
}
});
contextualMenu.add(pasteItem);
menuItemCount++;
}
if (closestConnections.size() > 0) {
contextualMenu.addSeparator();
menuItemCount++;
MenuItem deleteConnection = new MenuItem("Delete connection:");
deleteConnection.setEnabled(false);
contextualMenu.insert(deleteConnection, menuItemCount++);
for (Map.Entry> e : closestConnections
.entrySet()) {
final String connName = e.getKey();
for (StepManagerImpl[] cons : e.getValue()) {
final StepManagerImpl source = cons[0];
final StepManagerImpl target = cons[1];
MenuItem deleteItem =
new MenuItem(connName + "-->" + target.getName());
deleteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
m_visLayout.addUndoPoint();
source.disconnectStepWithConnection(target.getManagedStep(),
connName);
target.disconnectStepWithConnection(source.getManagedStep(),
connName);
if (m_visLayout.getSelectedSteps().size() > 0) {
m_visLayout.setSelectedSteps(new ArrayList());
}
m_visLayout.setEdited(true);
revalidate();
repaint();
m_visLayout.getMainPerspective().notifyIsDirty();
}
});
contextualMenu.add(deleteItem);
menuItemCount++;
}
}
}
if (menuItemCount > 0) {
contextualMenu.addSeparator();
menuItemCount++;
}
MenuItem noteItem = new MenuItem("New note");
noteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
initiateAddNote();
}
});
contextualMenu.add(noteItem);
this.add(contextualMenu);
// make sure that popup location takes current scaling into account
double z = m_visLayout.getZoomSetting() / 100.0;
double px = x * z;
double py = y * z;
contextualMenu.show(this, (int) px, (int) py);
}
/**
* Initiate the process of adding a note to the canvas
*/
protected void initiateAddNote() {
Note n = new Note();
StepManagerImpl noteManager = new StepManagerImpl(n);
StepVisual noteVisual = StepVisual.createVisual(noteManager);
m_visLayout.getMainPerspective().setPalleteSelectedStep(
noteVisual.getStepManager());
m_visLayout.setFlowLayoutOperation(LayoutOperation.ADDING);
m_visLayout.getMainPerspective().setCursor(
Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}
/**
* Popup the contextual menu for when a step is right clicked on
*
* @param step the step that was clicked on
* @param x the x coordiante to pop up at
* @param y the y coordinate to pop up at
*/
protected void stepContextualMenu(final StepVisual step, final int x,
final int y) {
PopupMenu stepContextMenu = new PopupMenu();
boolean executing = m_visLayout.isExecuting();
int menuItemCount = 0;
MenuItem edit = new MenuItem("Edit:");
edit.setEnabled(false);
stepContextMenu.insert(edit, menuItemCount);
menuItemCount++;
if (m_visLayout.getSelectedSteps().size() > 0) {
MenuItem copyItem = new MenuItem("Copy");
copyItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
m_visLayout.copySelectedStepsToClipboard();
} catch (WekaException ex) {
m_visLayout.getMainPerspective().showErrorDialog(ex);
}
m_visLayout.setSelectedSteps(new ArrayList());
}
});
stepContextMenu.add(copyItem);
copyItem.setEnabled(!executing);
menuItemCount++;
}
MenuItem deleteItem = new MenuItem("Delete");
deleteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
m_visLayout.addUndoPoint();
try {
m_visLayout.removeStep(step);
} catch (WekaException ex) {
m_visLayout.getMainPerspective().showErrorDialog(ex);
}
// step.getStepManager().clearAllConnections();
// LayoutPanel.this.remove(step);
String key =
step.getStepName() + "$"
+ step.getStepManager().getManagedStep().hashCode();
m_visLayout.getLogPanel().statusMessage(key + "|remove");
LayoutPanel.this.revalidate();
LayoutPanel.this.repaint();
m_visLayout.setEdited(true);
m_visLayout.getMainPerspective().notifyIsDirty();
m_visLayout
.getMainPerspective()
.getMainToolBar()
.enableWidget(
MainKFPerspectiveToolBar.Widgets.SELECT_ALL_BUTTON.toString(),
m_visLayout.getSelectedSteps().size() > 0);
}
});
deleteItem.setEnabled(!executing);
stepContextMenu.add(deleteItem);
menuItemCount++;
MenuItem nameItem = new MenuItem("Set name...");
nameItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String oldName = step.getStepName();
String name =
JOptionPane.showInputDialog(m_visLayout.getMainPerspective(),
"Enter a name for this step", oldName);
if (name != null) {
m_visLayout.renameStep(oldName, name);
m_visLayout.setEdited(true);
revalidate();
repaint();
}
}
});
nameItem.setEnabled(!executing);
stepContextMenu.add(nameItem);
menuItemCount++;
MenuItem configItem = new MenuItem("Configure...");
configItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
popupStepEditorDialog(step);
m_visLayout.getMainPerspective().notifyIsDirty();
}
});
configItem.setEnabled(!executing);
stepContextMenu.add(configItem);
menuItemCount++;
// connections
List outgoingConnTypes =
step.getStepManager().getManagedStep().getOutgoingConnectionTypes();
if (outgoingConnTypes != null && outgoingConnTypes.size() > 0) {
MenuItem connections = new MenuItem("Connections:");
connections.setEnabled(false);
stepContextMenu.insert(connections, menuItemCount);
menuItemCount++;
for (final String connType : outgoingConnTypes) {
MenuItem conItem = new MenuItem(connType);
conItem.setEnabled(!executing);
conItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
initiateConnection(connType, step, x, y);
m_visLayout.getMainPerspective().notifyIsDirty();
}
});
stepContextMenu.add(conItem);
menuItemCount++;
}
}
// Any interactive views?
Map interactiveViews =
step.getStepManager().getManagedStep().getInteractiveViewers();
Map interactiveViewsImpls =
step.getStepManager().getManagedStep().getInteractiveViewersImpls();
if (interactiveViews != null && interactiveViews.size() > 0) {
MenuItem actions = new MenuItem("Actions:");
actions.setEnabled(false);
stepContextMenu.insert(actions, menuItemCount++);
for (Map.Entry e : interactiveViews.entrySet()) {
String command = e.getKey();
String viewerClassName = e.getValue();
addInteractiveViewMenuItem(step, e.getKey(), !executing, e.getValue(),
null, stepContextMenu);
menuItemCount++;
}
} else if (interactiveViewsImpls != null
&& interactiveViewsImpls.size() > 0) {
MenuItem actions = new MenuItem("Actions:");
actions.setEnabled(false);
stepContextMenu.insert(actions, menuItemCount++);
for (Map.Entry e : interactiveViewsImpls
.entrySet()) {
String command = e.getKey();
StepInteractiveViewer impl = e.getValue();
addInteractiveViewMenuItem(step, e.getKey(), !executing, null, impl,
stepContextMenu);
menuItemCount++;
}
}
// perspective stuff...
final List perspectives =
m_visLayout.getMainPerspective().getMainApplication()
.getPerspectiveManager().getVisiblePerspectives();
if (perspectives.size() > 0
&& step.getStepManager().getManagedStep() instanceof Loader) {
final weka.core.converters.Loader theLoader =
((Loader) step.getStepManager().getManagedStep()).getLoader();
boolean ok = true;
if (theLoader instanceof FileSourcedConverter) {
String fileName =
((FileSourcedConverter) theLoader).retrieveFile().getPath();
fileName = m_visLayout.environmentSubstitute(fileName);
File tempF = new File(fileName);
String fileNameFixedPathSep = fileName.replace(File.separatorChar, '/');
if (!tempF.isFile()
&& this.getClass().getClassLoader().getResource(fileNameFixedPathSep) == null) {
ok = false;
}
}
if (ok) {
stepContextMenu.addSeparator();
menuItemCount++;
if (perspectives.size() > 1) {
MenuItem sendToAllPerspectives =
new MenuItem("Send to all perspectives");
menuItemCount++;
sendToAllPerspectives.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
loadDataAndSendToPerspective(theLoader, perspectives, -1);
}
});
stepContextMenu.add(sendToAllPerspectives);
}
Menu sendToPerspective = new Menu("Send to perspective...");
stepContextMenu.add(sendToPerspective);
menuItemCount++;
for (int i = 0; i < perspectives.size(); i++) {
final int pIndex = i;
if (perspectives.get(i).acceptsInstances()
&& !perspectives.get(i).getPerspectiveID()
.equalsIgnoreCase(KFDefaults.MAIN_PERSPECTIVE_ID)) {
String pName = perspectives.get(i).getPerspectiveTitle();
final MenuItem pI = new MenuItem(pName);
pI.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
loadDataAndSendToPerspective(theLoader, perspectives, pIndex);
}
});
sendToPerspective.add(pI);
}
}
}
}
if (menuItemCount > 0) {
// make sure that popup location takes current scaling into account
double z = m_visLayout.getZoomSetting() / 100.0;
double px = x * z;
double py = y * z;
this.add(stepContextMenu);
stepContextMenu.show(this, (int) px, (int) py);
}
}
private synchronized void loadDataAndSendToPerspective(
final weka.core.converters.Loader loader,
final List perspectives, final int perspectiveIndex) {
if (m_perspectiveDataLoadThread == null) {
m_perspectiveDataLoadThread = new Thread() {
@Override
public void run() {
try {
if (loader instanceof EnvironmentHandler) {
((EnvironmentHandler) loader).setEnvironment(m_visLayout
.getEnvironment());
loader.reset();
}
m_visLayout.getLogPanel().statusMessage(
"@!@[KnowledgeFlow]|Sending data to perspective(s)...");
Instances data = loader.getDataSet();
if (data != null) {
m_visLayout.getMainPerspective().getMainApplication()
.showPerspectivesToolBar();
// need to disable all the perspective buttons on the toolbar
/*
* m_visLayout.getMainPerspective().getMainApplication()
* .getPerspectiveManager().disableAllPerspectiveTabs();
*/
if (perspectiveIndex < 0) {
// send to all
for (Perspective p : perspectives) {
if (p.acceptsInstances()) {
p.setInstances(data);
m_visLayout.getMainPerspective().getMainApplication()
.getPerspectiveManager()
.setEnablePerspectiveTab(p.getPerspectiveID(), true);
}
}
} else {
perspectives.get(perspectiveIndex).setInstances(data);
m_visLayout
.getMainPerspective()
.getMainApplication()
.getPerspectiveManager()
.setActivePerspective(
perspectives.get(perspectiveIndex).getPerspectiveID());
m_visLayout
.getMainPerspective()
.getMainApplication()
.getPerspectiveManager()
.setEnablePerspectiveTab(
perspectives.get(perspectiveIndex).getPerspectiveID(), true);
}
}
} catch (Exception ex) {
m_visLayout.getMainPerspective().showErrorDialog(ex);
} finally {
// re-enable the perspective buttons
/*
* m_visLayout.getMainPerspective().getMainApplication()
* .getPerspectiveManager().enableAllPerspectiveTabs();
*/
m_perspectiveDataLoadThread = null;
m_visLayout.getLogPanel().statusMessage("@!@[KnowledgeFlow]|OK");
}
}
};
m_perspectiveDataLoadThread.setPriority(Thread.MIN_PRIORITY);
m_perspectiveDataLoadThread.start();
}
}
/**
* Initiate the process of connecting two steps.
*
* @param connType the type of the connection to create
* @param source the source step of the connection
* @param x the x coordinate at which the connection starts
* @param y the y coordinate at which the connection starts
*/
protected void initiateConnection(String connType, StepVisual source, int x,
int y) {
// unselect any selected steps on the layaout
if (m_visLayout.getSelectedSteps().size() > 0) {
m_visLayout.setSelectedSteps(new ArrayList());
}
List connectableForConnType =
m_visLayout.findStepsThatCanAcceptConnection(connType);
for (StepManagerImpl sm : connectableForConnType) {
sm.getStepVisual().setDisplayConnectors(true);
}
if (connectableForConnType.size() > 0) {
source.setDisplayConnectors(true);
m_visLayout.setEditStep(source);
m_visLayout.setEditConnection(connType);
Point closest = source.getClosestConnectorPoint(new Point(x, y));
m_currentX = (int) closest.getX();
m_currentY = (int) closest.getY();
m_oldX = m_currentX;
m_oldY = m_currentY;
Graphics2D gx = (Graphics2D) this.getGraphics();
gx.setXORMode(java.awt.Color.white);
gx.drawLine(m_currentX, m_currentY, m_currentX, m_currentY);
gx.dispose();
m_visLayout.setFlowLayoutOperation(LayoutOperation.CONNECTING);
}
revalidate();
repaint();
m_visLayout.getMainPerspective().notifyIsDirty();
}
/**
* Add a menu item to a contextual menu for accessing any interactive viewers
* that a step might provide
*
* @param step the step in question
* @param entryText the text of the menu entry
* @param enabled true if the menu entry is to be enabled
* @param viewerClassName the fully qualified name of the viewer that is
* associated with the menu entry
* @param viewerImpl an instance of the viewer itself. If null, then the fully
* qualified viewer class name will be used to instantiate an
* instance
* @param stepContextMenu the menu to add the item to
*/
protected void addInteractiveViewMenuItem(final StepVisual step,
String entryText, boolean enabled, final String viewerClassName,
final StepInteractiveViewer viewerImpl, PopupMenu stepContextMenu) {
MenuItem viewItem = new MenuItem(entryText);
viewItem.setEnabled(enabled);
viewItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
popupStepInteractiveViewer(step, viewerClassName, viewerImpl);
}
});
stepContextMenu.add(viewItem);
}
/**
* Popup a dialog containing a particular interactive step viewer
*
* @param step the step that provides the viewer
* @param viewerClassName the fully qualified name of the viewer
* @param viewerImpl the actual instance of the viewer
*/
protected void popupStepInteractiveViewer(StepVisual step,
String viewerClassName, StepInteractiveViewer viewerImpl) {
try {
Object viewer =
viewerClassName != null ? WekaPackageClassLoaderManager
.objectForName(viewerClassName) : viewerImpl;
// viewerClassName != null ? Beans.instantiate(this.getClass()
// .getClassLoader(), viewerClassName) : viewerImpl;
if (!(viewer instanceof StepInteractiveViewer)) {
throw new WekaException("Interactive step viewer component "
+ viewerClassName + " must implement StepInteractiveViewer");
}
if (!(viewer instanceof JComponent)) {
throw new WekaException("Interactive step viewer component "
+ viewerClassName + " must be a JComponent");
}
String viewerName = ((StepInteractiveViewer) viewer).getViewerName();
((StepInteractiveViewer) viewer).setStep(step.getStepManager()
.getManagedStep());
((StepInteractiveViewer) viewer).setMainKFPerspective(m_visLayout
.getMainPerspective());
JFrame jf = Utils.getWekaJFrame(viewerName, this);
((StepInteractiveViewer) viewer).setParentWindow(jf);
((StepInteractiveViewer) viewer).init();
jf.setLayout(new BorderLayout());
jf.add((JComponent) viewer, BorderLayout.CENTER);
jf.pack();
jf.setLocationRelativeTo(SwingUtilities.getWindowAncestor(this));
jf.setVisible(true);
((StepInteractiveViewer) viewer).nowVisible();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
m_visLayout.getMainPerspective().showErrorDialog(e);
}
}
/**
* Popup an editor dialog for a given step
*
* @param step the step to popup the editor dialog for
*/
protected void popupStepEditorDialog(StepVisual step) {
String custEditor =
step.getStepManager().getManagedStep().getCustomEditorForStep();
StepEditorDialog toPopup = null;
if (custEditor != null && custEditor.length() > 0) {
try {
Object custPanel = WekaPackageClassLoaderManager.objectForName(custEditor);
// Beans.instantiate(getClass().getClassLoader(), custEditor);
if (!(custPanel instanceof StepEditorDialog)) {
throw new WekaException(
"Custom editor must be a subclass of StepEditorDialog");
}
toPopup = ((StepEditorDialog) custPanel);
} catch (Exception ex) {
m_visLayout.getMainPerspective().showErrorDialog(ex);
ex.printStackTrace();
}
} else {
// create an editor based on the GenericObjectEditor
toPopup = new GOEStepEditorDialog();
toPopup.setMainPerspective(m_visLayout.getMainPerspective());
}
final JDialog d =
new JDialog((java.awt.Frame) getTopLevelAncestor(),
ModalityType.DOCUMENT_MODAL);
d.setLayout(new BorderLayout());
d.getContentPane().add(toPopup, BorderLayout.CENTER);
final StepEditorDialog toPopupC = toPopup;
d.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosing(java.awt.event.WindowEvent e) {
d.dispose();
}
});
toPopup.setParentWindow(d);
toPopup.setEnvironment(m_visLayout.getEnvironment());
toPopup.setMainPerspective(m_visLayout.getMainPerspective());
toPopup.setStepToEdit(step.getStepManager().getManagedStep());
toPopup.setClosingListener(new StepEditorDialog.ClosingListener() {
@Override
public void closing() {
if (toPopupC.isEdited()) {
m_visLayout.setEdited(true);
}
}
});
d.pack();
d.setLocationRelativeTo(m_visLayout.getMainPerspective());
d.setVisible(true);
}
private int snapToGrid(int val) {
int r = val % m_gridSpacing;
val /= m_gridSpacing;
if (r > (m_gridSpacing / 2)) {
val++;
}
val *= m_gridSpacing;
return val;
}
/**
* Snaps selected steps to the grid
*/
protected void snapSelectedToGrid() {
List selected = m_visLayout.getSelectedSteps();
for (StepVisual s : selected) {
int x = s.getX();
int y = s.getY();
s.setX(snapToGrid(x));
s.setY(snapToGrid(y));
}
revalidate();
repaint();
m_visLayout.setEdited(true);
m_visLayout.getMainPerspective().notifyIsDirty();
}
private void highlightSubFlow(int startX, int startY, int endX, int endY) {
java.awt.Rectangle r =
new java.awt.Rectangle((startX < endX) ? startX : endX,
(startY < endY) ? startY : endY, Math.abs(startX - endX),
Math.abs(startY - endY));
List selected = m_visLayout.findSteps(r);
m_visLayout.setSelectedSteps(selected);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy