/*
* This file is part of Alida, a Java library for
* Advanced Library for Integrated Development of Data Analysis Applications.
*
* Copyright (C) 2010 - @YEAR@
*
* 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 <http://www.gnu.org/licenses/>.
*
* Fore more information on Alida, visit
*
* http://www.informatik.uni-halle.de/alida/
*
*/
/*
* Most recent change(s):
*
* $Rev: 6237 $
* $Date: 2012-11-14 19:10:54 +0100 (Mi, 14 Nov 2012) $
* $Author: moeller $
*
*/
package de.unihalle.informatik.Alida.grappa;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import org.w3c.dom.Document;
import com.mxgraph.io.mxCodec;
import com.mxgraph.io.mxCodecRegistry;
import com.mxgraph.io.mxObjectCodec;
import com.mxgraph.model.mxCell;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUtils;
import com.mxgraph.util.mxXmlUtils;
import com.mxgraph.view.mxGraph;
import de.unihalle.informatik.Alida.exceptions.ALDWorkflowException;
import de.unihalle.informatik.Alida.operator.ALDOperatorLocation;
import de.unihalle.informatik.Alida.workflows.ALDWorkflow;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowClassEvent;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowClassEvent.ALDWorkflowClassEventType;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowStorageInfo;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowClassEventListener;
/**
* Main frame of Grappa.
* @author Birgit Moeller
*/
public class ALDGrappaWorkbench extends JTabbedPane
implements ActionListener, ALDWorkflowClassEventListener, ChangeListener {
protected ALDGrappaFrame mainFrame;
protected JFrame renameWin;
protected JTextField renameTextField;
protected File lastLoadFilePath = new File(System.getProperty("user.home"));
protected File lastSaveFilePath = new File(System.getProperty("user.home"));
/**
* Flag to indicate if progress events are to be shown in window or not.
*/
private boolean showProgressEvents = true;
/**
* List of workflows currently managed.
*/
protected LinkedList<ALDGrappaWorkbenchTab> workflows =
new LinkedList<ALDGrappaWorkbenchTab>();
/**
* Default constructor
*/
public ALDGrappaWorkbench(ALDGrappaFrame frame) {
super();
// add ourselves as listener for change events
this.addChangeListener(this);
this.mainFrame = frame;
this.initRenameWin();
// register for load events
ALDWorkflow.addALDWorkflowClassEventListener(this);
// init an empty workflow on start-up
this.initNewWorkflow();
this.setSize(400, 400);
}
/**
* Method to turn on/off display of progress events in status field.
* @param flag If true, progress event messages are displayed, otherwise not.
*/
public void setShowProgressEvents(boolean flag) {
this.showProgressEvents = flag;
}
/**
* Method to request whether to display progress events or not.
* @return True, if progress event messages are to be shown.
*/
public boolean showProgressEvents() {
return this.showProgressEvents;
}
/**
* Adds a new workflow (tab) to the workbench.
* @return True, if addition was successful.
*/
public boolean addNewWorkflow() {
return this.initNewWorkflow();
}
/**
* Returns name of currently active workflow.
* @return Name of workflow.
*/
public String getWorkflowName() {
String workflowName = "<no workflow present>";
if (this.getSelectedComponent() != null)
workflowName =
((ALDGrappaWorkbenchTab)this.getSelectedComponent()).
getWorkflowTitle();
return workflowName;
}
/**
* Rename the currently active workflow.
*/
public void renameWorkflow() {
this.renameTextField.setText(this.getWorkflowName());
this.renameWin.setVisible(true);
}
/**
* Removes the currently active workflow.
*/
public void removeWorkflow() {
// we can only remove workflows if there are any...
if (this.getSelectedComponent() != null) {
// post system message
this.mainFrame.postSystemStatusMessage("closing workflow <"
+ ((ALDGrappaWorkbenchTab)this.getSelectedComponent()).
workflowTitle + ">...");
this.remove(this.getSelectedComponent());
}
}
/**
* Loads a new workflow from file(s).
*/
public void loadWorkflow() {
final File file = showLoadFileSelectDialog();
if (file == null || !file.exists()) {
String name = "";
if (file != null) {
name = "\"" + file.getAbsolutePath() + "\"";
}
JOptionPane.showMessageDialog(this.mainFrame,
"The selected file " + name + " does not exist!",
"Error loading workflow", JOptionPane.ERROR_MESSAGE);
return;
}
try {
ALDWorkflow.load(file, true);
} catch (ALDWorkflowException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// try {
// Thread tSave = new Thread() {
// @Override
// public void run() {
// saveWorkflow(file);
// }
// };
// tSave.start();
// tSave.join();
// JOptionPane.showMessageDialog(null, "Save successful",
// "Message", JOptionPane.OK_CANCEL_OPTION);
// } catch (InterruptedException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }
}
/**
* Saves the currently active workflow to file(s).
*/
public void saveWorkflow() {
final File file = showSaveFileSelectDialog();
if (file != null) {
// post system message
this.mainFrame.postSystemStatusMessage("saving workflow <"
+ ((ALDGrappaWorkbenchTab)this.getSelectedComponent()).workflowTitle
+ ">...");
((ALDGrappaWorkbenchTab)this.getSelectedComponent()).saveWorkflow(file);
}
}
/**
* Run the currently active workflow.
*/
public void runWorkflow() {
((ALDGrappaWorkbenchTab)this.getSelectedComponent()).runWorkflow();
}
public void interruptWorkflowExecution() {
((ALDGrappaWorkbenchTab)this.getSelectedComponent()).interruptExecution();
}
public void addNodeToWorkflow(ALDOperatorLocation opNamePath) {
((ALDGrappaWorkbenchTab)this.getSelectedComponent()).
createNewWorkflowNode(opNamePath, -1, -1);
}
public ALDOperatorLocation popRecentlySelectedOperatorPath() {
return this.mainFrame.popRecentlySelectedOperatorLocation();
}
public void clearTreeSelection() {
this.mainFrame.opTree.clearSelection();
}
/**
* Closes the main window after cleaning up.
*/
public void quit() {
for (ALDGrappaWorkbenchTab workflow : this.workflows) {
workflow.quit();
}
this.mainFrame.setVisible(false);
this.renameWin.setVisible(false);
}
protected boolean initNewWorkflow() {
// init the new workflow graph
// mxGraph newFlow = new mxGraph();
ALDGrappaWorkbenchGraph newFlow = new ALDGrappaWorkbenchGraph();
return this.initNewWorkflow(newFlow);
}
protected boolean initNewWorkflow(ALDGrappaWorkbenchGraph graph) {
graph.setMinimumGraphSize(new mxRectangle(0, 0, 500, 500));
graph.setAllowDanglingEdges(false);
graph.setCellsEditable(false);
// instantiate the associated tab in window
ALDGrappaWorkbenchTab newContent = this.initNewTab(graph);
newContent.setFocusable(true);
newContent.setToolTips(true);
this.workflows.add(newContent);
this.addTab(newContent.workflowTitle,newContent);
// post system message
this.mainFrame.postSystemStatusMessage("adding new workflow <"
+ newContent.workflowTitle + ">...");
return true;
// newComp.getGraphControl().addMouseListener(
// new GraphMouseAdapter(this));
// newComp.getConnectionHandler().addListener(mxEvent.CONNECT,elist);
// newFlow.addListener(mxEvent.CONNECT_CELL, elist);
// {
// ...
// @Override
// public boolean isCellFoldable(Object cell, boolean collapse) {
// return false;
// }
// };
// EdgeConnectListener elist = new EdgeConnectListener();
// add graph to the component and finally to the panel
// HIER!!!
// graphComponent.getConnectionHandler().mouseReleased(arg0)
}
protected ALDGrappaWorkbenchTab initNewTab(ALDGrappaWorkbenchGraph graph) {
return new ALDGrappaWorkbenchTab(this,graph);
}
/**
* Setup of the window for renaming workflows.
*/
protected void initRenameWin() {
this.renameWin = new JFrame("Rename workflow");
JPanel mainPanel = new JPanel();
mainPanel.add(new JLabel("New workflow name:"));
this.renameTextField = new JTextField(25);
this.renameTextField.setColumns(15);
this.renameTextField.setEditable(true);
mainPanel.add(this.renameTextField);
JButton okButton = new JButton("Ok");
okButton.setActionCommand("rename_button_ok");
okButton.addActionListener(this);
JButton cancelButton = new JButton("Cancel");
cancelButton.setActionCommand("rename_button_cancel");
cancelButton.addActionListener(this);
mainPanel.add(okButton);
mainPanel.add(cancelButton);
this.renameWin.add(mainPanel);
this.renameWin.setSize(200,115);
this.renameWin.setResizable(false);
}
protected void initReloadedWorkflow(mxGraph _graph, ALDWorkflow _flow) {
// check how many workflow tabs are currently present, if there
// is only a single empty (unused) one, delete that tab first
if ( this.workflows.size() == 1
&& !this.workflows.get(0).workflowHasNodes()
&& this.workflows.get(0).getWorkflowTitle()
== ALDWorkflow.untitledWorkflowName) {
this.removeWorkflow();
}
// instantiate the associated tab in window
ALDGrappaWorkbenchTab newContent =
this.initReloadedTab(_graph, _flow);
newContent.setFocusable(true);
newContent.setToolTips(true);
this.workflows.add(newContent);
this.addTab(newContent.getWorkflowTitle(),newContent);
// set reloaded workflow active
this.setSelectedIndex(this.getComponentCount()-1);
}
protected void renameWorkflow(String newName) {
((ALDGrappaWorkbenchTab)this.getSelectedComponent()).setWorkflowTitle(
newName);
// this.setTitleAt(this.getSelectedIndex(), newName);
}
protected ALDGrappaWorkbenchTab initReloadedTab(mxGraph _graph,
ALDWorkflow _flow) {
return new ALDGrappaWorkbenchTab(this,_graph,_flow);
}
/**
* Show file dialog to load a workflow.
* @return File path, might be null.
*/
protected File showLoadFileSelectDialog() {
File file = null;
JFileChooser fileDialog = new JFileChooser();
fileDialog.setDialogType(JFileChooser.OPEN_DIALOG);
fileDialog.setDialogTitle("Load workflow...");
fileDialog.setApproveButtonText("Load");
// configure file ending
fileDialog.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.getName().toLowerCase().endsWith(
ALDWorkflow.workflowXMLFileExtension)
|| f.isDirectory();
}
@Override
public String getDescription() {
return "Alida Workflow (*." +
ALDWorkflow.workflowXMLFileExtension + ")";
}
});
fileDialog.setAcceptAllFileFilterUsed(false);
fileDialog.setFileHidingEnabled(false);
fileDialog.updateUI();
fileDialog.setCurrentDirectory(this.lastLoadFilePath);
fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
if (fileDialog.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
file = fileDialog.getSelectedFile();
this.lastLoadFilePath = file;
}
return file;
}
/**
* Show file dialog to save a workflow.
* @return File path, might be null.
*/
protected File showSaveFileSelectDialog() {
// get name of workflow for filename default
String title =
((ALDGrappaWorkbenchTab)this.getSelectedComponent()).getWorkflowTitle();
File file = null;
JFileChooser fileDialog = new JFileChooser();
fileDialog.setDialogType(JFileChooser.OPEN_DIALOG);
fileDialog.setDialogTitle("Save workflow...");
fileDialog.setApproveButtonText("Save");
fileDialog.setSelectedFile(new File(title + ".awf"));
// configure file ending
fileDialog.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.getName().toLowerCase().endsWith(
ALDWorkflow.workflowXMLFileExtension)
|| f.isDirectory();
}
@Override
public String getDescription() {
return "Alida Workflow (*." +
ALDWorkflow.workflowXMLFileExtension + ")";
}
});
fileDialog.setFileHidingEnabled(false);
fileDialog.updateUI();
fileDialog.setCurrentDirectory(this.lastSaveFilePath);
fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
if (fileDialog.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
file = fileDialog.getSelectedFile();
// check if file has correct ending
if (!(file.getAbsolutePath().endsWith(
"." + ALDWorkflow.workflowXMLFileExtension))) {
String newFilename = file.getAbsolutePath() + "." + ALDWorkflow.workflowXMLFileExtension;
if ( JOptionPane.showOptionDialog(null,
"Filename " + " does not end with ." + ALDWorkflow.workflowXMLFileExtension +
", use " + newFilename + " instead ?",
"non standard extension",
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null, null, null) == 0 ) {
file = new File(newFilename);
}
}
this.lastSaveFilePath = file;
if (file.exists()) {
if ( JOptionPane.showOptionDialog(null,
"File " + file.getAbsolutePath() + " exists, override?",
"file exists",
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null, null, null) != 0 ) {
return null;
}
}
}
return file;
}
protected void handleWorkflowRenameEvent(String newName) {
this.setTitleAt(this.getSelectedIndex(), newName);
}
protected void handleLoadWorkflowEvent(ALDWorkflowClassEvent event) {
ALDWorkflowStorageInfo info = (ALDWorkflowStorageInfo)event.getId();
ALDWorkflow alidaWorkflow = info.getWorkflow();
String file = info.getFilename();
file = file + ".gui";
// post system message
this.mainFrame.postSystemStatusMessage("loading workflow <"
+ alidaWorkflow.getName() + ">...");
// read the file
Document document;
try {
mxCodecRegistry.addPackage(
"de.unihalle.informatik.Alida.grappa.ALDGrappaNodeInfo");
mxCodecRegistry.register(new mxObjectCodec(new ALDGrappaNodeInfo()));
mxCodecRegistry.addPackage(
"de.unihalle.informatik.Alida.grappa.ALDGrappaNodePortInfo");
mxCodecRegistry.register(new mxObjectCodec(new ALDGrappaNodePortInfo()));
document = mxXmlUtils.parseXml(mxUtils.readFile(file));
mxCodec codec = new mxCodec(document);
// read the graph
mxGraph newGraph = new mxGraph();
codec.decode(document.getDocumentElement(), newGraph.getModel());
// for (Object child : newGraph.getChildCells(newGraph.getDefaultParent())) {
// mxCell cell = (mxCell) child;
// String name = cell.getValue().toString();
// Integer id = ((ALDGrappaNodeInfo)cell.getValue()).getRefID();
// if (cell.isVertex()) {
// System.out.println("- Loading node " + name + " with id = " + id);
// }
// else {
// System.out.println("- Loading edge " + name + " with id = " + id);
// }
// }
// recover tab
this.initReloadedWorkflow(newGraph, alidaWorkflow);
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "Loading workflow graphics failed!",
"Workflow Load Error", JOptionPane.ERROR_MESSAGE);
return;
}
// // set all operator configurations loaded and also register edges
// LinkedList<String> missingOperatorConfigurations = new LinkedList<String>();
// for (Object child : opGraph.getChildCells(opGraph.getDefaultParent())) {
// mxCell cell = (mxCell) child;
// if (cell.isVertex()) {
// String name = cell.getValue().toString();
// if ( operatorMap.get(name) != null ) {
// registerNode(cell, operatorMap.get(name));
// } else {
// // add newly instantiated (not configured) operator
//
// if ( loadindConfigOk ) {
// missingOperatorConfigurations.add( name);
// }
// String opName = cell.getId().split("@")[0];
//
// try {
// registerNode(cell,(ALDOperator) (Class.forName(opName).newInstance()));
// } catch (Exception e) {
// // may be show options pane
// JOptionPane.showMessageDialog(null, "Could not instantiate operator " +
// opName,
// "Warning", JOptionPane.OK_CANCEL_OPTION);
// }
// }
//
// } else if (cell.isEdge()) {
// registerEdge(cell);
// }
// }
//
// if ( ! missingOperatorConfigurations.isEmpty() ) {
// String missingNames = new String();
// for (String opName : missingOperatorConfigurations) {
// missingNames += "<br>" +
// opName;
// }
// JOptionPane.showMessageDialog(null, "<html>Could not load configurations for operators " +
// missingNames + "</html>",
// "Warning", JOptionPane.OK_CANCEL_OPTION);
// }
//
// this.workFlowLoaded = true;
// // validate work flow to color nodes correctly
// LinkedList<mxCell> allNodes =
// this.opGraph.getAllNodesTopologicallyOrdered();
// LinkedList<mxCell> processed = new LinkedList<mxCell>();
// for (mxCell node : allNodes) {
// if (!processed.contains(node)) {
// processed.add(node);
// boolean readyToRun = this.opDescrMap.get(node).
// getOperatorControlFrame().validateParameters(false, false);
// if (readyToRun)
// this.setToConfigured(node.getValue().toString(), true);
// else {
// LinkedList<mxCell> successors =
// this.opGraph.getSubsequentNodes(node);
// for (mxCell sNode : successors) {
// this.setToConfigured(sNode.getValue().toString(), false);
// processed.add(sNode);
// }
// }
// }
// }
// } catch (FileNotFoundException e) {
// this.actionPerformed(new ActionEvent(this,1,"new"));
// JOptionPane.showMessageDialog(null,
// "Cannot load operator configurations from " +
// file.getAbsolutePath() + "." + opsXMLFileExtension
// +": file not found!", "Error", JOptionPane.ERROR_MESSAGE);
// } catch (ALDOperatorException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
//
// }
//
}
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("rename_button_ok")) {
String newName = this.renameTextField.getText();
this.renameWin.setVisible(false);
String oldName = this.getWorkflowName();
this.mainFrame.postSystemStatusMessage("renaming workflow <"
+ oldName + "> to <" + newName + ">...");
this.renameWorkflow(newName);
this.mainFrame.postWorkflowStatusMessage(newName, "ready");
}
else if (command.equals("rename_button_cancel")) {
this.renameWin.setVisible(false);
}
}
@Override
public void handleALDWorkflowClassEvent(ALDWorkflowClassEvent event) {
// extract event data
ALDWorkflowClassEventType type = event.getEventType();
// ignore all events except load events
if (type.equals(
ALDWorkflowClassEvent.ALDWorkflowClassEventType.LOAD_WORKFLOW)) {
handleLoadWorkflowEvent(event);
}
}
@Override
public synchronized void stateChanged(ChangeEvent e) {
// this event is triggered if the active tab changes or a tab is
// closed; then a status update is required
String statusMsg = "ready";
if (e instanceof ALDGrappaWorkflowTabChangeEvent) {
statusMsg = ((ALDGrappaWorkflowTabChangeEvent)e).getEventMessage();
}
this.mainFrame.postWorkflowStatusMessage(
this.getWorkflowName(), statusMsg);
}
}