/**
* Copyright 1999-2009 The Pegadi Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pegadi.storysketch;
import org.apache.xerces.parsers.DOMParser;
import org.apache.xml.serialize.DOMSerializer;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.SerializerFactory;
import org.jgraph.JGraph;
import org.jgraph.event.GraphModelEvent;
import org.jgraph.event.GraphModelListener;
import org.jgraph.event.GraphSelectionEvent;
import org.jgraph.event.GraphSelectionListener;
import org.jgraph.graph.*;
import org.jgraph.plaf.basic.BasicGraphUI;
import org.pegadi.storysketch.cells.*;
import org.pegadi.storysketch.views.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import javax.swing.*;
import javax.swing.event.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.*;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterJob;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.text.DateFormat;
import java.util.*;
public class StorySketchPanel extends JPanel implements GraphSelectionListener, KeyListener, Printable {
private final Logger log = LoggerFactory.getLogger(getClass());
JGraph graph;
JTree tree;
StorySketchApplication application;
public static BrowserLauncher launcher;
// Undo Manager
protected GraphUndoManager undoManager;
// Actions which Change State
protected Action undo, redo, remove, group, ungroup, tofront, toback,
cut, copy, paste, xmlSave,xmlLoad, print, feedback;
ButtonGroup buttonGroup = new ButtonGroup();
JToggleButton selectButton;
JToggleButton personButton;
JToggleButton noteButton;
JToggleButton quoteButton;
JToggleButton eventButton;
JToggleButton appointmentButton;
JToggleButton archiveButton;
JToggleButton conflictButton;
JToggleButton placeButton;
JToggleButton edgeButton;
JEditorPane info = new JEditorPane("text/html","<html></html>");
String helpText = "<html><body bgcolor=\"#FFFFFF\"><b>Saksskisse</b><br>Klikk på et objekt for å velge det<br>Dobbeltklikk for å redigere<br>Velg verktøy på toppen for å sette inn nytt objekt</body></html>";
OverviewPanel overview;
public StorySketchPanel() {
setLayout(new BorderLayout());
GraphModel model = new StorySketchGraphModel();
graph = new StorySketchGraph(model);
//graph.setTransferHandler(new StorySketchTransferHandler());
graph.setDropTarget(new DropTarget() {
public void drop(DropTargetDropEvent dtde) {
graph_drop(dtde);
}
});
graph.setSelectNewCells(true);
setStorySketchApplication(new DefaultStorySketchApplication());
JScrollPane graphScroll = new JScrollPane(graph);
overview = new OverviewPanel(graph, graphScroll);
overview.setMinimumSize(new Dimension(70, 80));
overview.setPreferredSize(new Dimension(70, 80));
graph.setAntiAliased(true);
graph.setDoubleBuffered(false);
graph.setMarqueeColor(Color.DARK_GRAY);
graph.getSelectionModel().addGraphSelectionListener(new GraphSelectionListener() {
public void valueChanged(GraphSelectionEvent e) {
updateInfo(e);
}
});
info.setEditable(false);
info.setText(helpText);
info.setCaretPosition(0);
info.setPreferredSize(new Dimension(0,0));
info.addHyperlinkListener(new HyperlinkListener() {
public void hyperlinkUpdate(HyperlinkEvent e) {
info_hyperlinkUpdate(e);
}
});
// Set up the graph tree
//GraphTreeModel gtModel = new GraphTreeModel(graph.getModel());
//tree = new JTree(gtModel);
//graph.getModel().addGraphModelListener(gtModel);
//tree.setRootVisible(false);
graph.setInvokesStopCellEditing(true);
graph.setGridEnabled(true);
graph.setGridVisible(true);
graph.setGridSize(8);
graph.setGridColor(new Color(225,225,225));
graph.setGridMode(JGraph.LINE_GRID_MODE);
//graph.setBackground(new Color(240,240,240));
undoManager = new GraphUndoManager() {
// Override Superclass
public void undoableEditHappened(UndoableEditEvent e) {
// First Invoke Superclass
super.undoableEditHappened(e);
// Then Update Undo/Redo Buttons
updateHistoryButtons();
}
};
// Update ToolBar based on Selection Changes
graph.getSelectionModel().addGraphSelectionListener(this);
// Listen for Delete Keystroke when the Graph has Focus
graph.addKeyListener(this);
// Construct Panel
JPanel graphPanel = new JPanel(new BorderLayout());
// Add a ToolBar
graphPanel.add(createInsertBar(), BorderLayout.NORTH);
JToolBar tb = createToolBar();
tb.setOrientation(SwingConstants.VERTICAL);
graphPanel.add(tb, BorderLayout.EAST);
graphPanel.add(graphScroll, BorderLayout.CENTER);
// Add the Graph as Center Component
JSplitPane bottomSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
new JScrollPane(info),
overview);
JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
graphPanel,
bottomSplit);
split.setDividerLocation(0.7);
split.setResizeWeight(1);
bottomSplit.setDividerLocation(0.5);
bottomSplit.setResizeWeight(1);
add(split, BorderLayout.CENTER);
setModel(model);
}
public void graph_drop(DropTargetDropEvent dtde) {
Transferable t = dtde.getTransferable();
DataFlavor[] flavors = t.getTransferDataFlavors();
boolean canImport = false;
for (DataFlavor flavor : flavors) {
if (DataFlavor.stringFlavor.equals(flavor)) {
canImport = true;
}
}
if(!canImport) {
return;
}
try {
dtde.acceptDrop(DnDConstants.ACTION_COPY);
String str = (String)t.getTransferData(DataFlavor.stringFlavor);
JMenu insert = createInsertMenu(dtde.getLocation(), str);
JPopupMenu menu = new JPopupMenu();
menu.add(insert);
menu.show(graph, (int) dtde.getLocation().getX(), (int) dtde.getLocation().getY());
}catch (UnsupportedFlavorException ignored) {
} catch (IOException ignored) {
}
}
public GraphModel getModel() {
return graph.getModel();
}
public void setModel(GraphModel model) {
graph.setModel(model);
overview.setModel(model);
model.addUndoableEditListener(undoManager);
updateHistoryButtons();
}
public void updateInfo(GraphSelectionEvent e) {
Object c = e.getCell();
if(graph.getSelectionCell() == null) {
info.setText(helpText);
info.setCaretPosition(0);
}
else if(c instanceof StorySketchCell) {
StorySketchCell sc = (StorySketchCell) c;
info.setText(sc.toHTML());
info.setCaretPosition(0);
}
}
//
// PopupMenu
//
public JPopupMenu createPopupMenu(final MouseEvent event, final Object cell) {
final Point pt = event.getPoint();
JPopupMenu menu = new JPopupMenu();
if (cell != null) {
// Edit
menu.add(new AbstractAction("Rediger") {
public void actionPerformed(ActionEvent e) {
graph.startEditingAtCell(cell);
}
});
if(cell instanceof PersonCell) {
PersonCell pc = (PersonCell) cell;
final Person p = (Person) pc.getUserObject();
if(p.getEmail().length() > 0) {
final String address = (p.getName().length() > 1) ? p.getName() +" <" +p.getEmail() +">" : p.getEmail();
menu.add(new AbstractAction("Send epost") {
public void actionPerformed(ActionEvent e) {
application.sendMail(address, "", "");
}
});
menu.add(new AbstractAction("Send artikkel") {
public void actionPerformed(ActionEvent e) {
application.sendArticleAsMail(address);
}
});
}
}
} else {
JMenu insert = createInsertMenu(pt,"");
menu.add(insert);
}
// Remove
if (!graph.isSelectionEmpty()) {
menu.add(new AbstractAction("Slett") {
public void actionPerformed(ActionEvent e) {
remove.actionPerformed(e);
}
});
}
return menu;
}
public JMenu createInsertMenu(final Point pt, final String str) {
JMenu insert = new JMenu("Sett inn");
// Person
insert.add(new AbstractAction("Person") {
public void actionPerformed(ActionEvent ev) {
Person p = new Person();
p.setName(str);
insertPerson(pt, p);
}
});
// Note
insert.add(new AbstractAction("Notat") {
public void actionPerformed(ActionEvent ev) {
Note n = new Note();
n.setNotes(str);
insertNote(pt, n);
}
});
// Quote
insert.add(new AbstractAction("Sitat") {
public void actionPerformed(ActionEvent ev) {
Quote q = new Quote();
q.setQuote(str);
insertQuote(pt, q);
}
});
// Avtale
insert.add(new AbstractAction("Avtale") {
public void actionPerformed(ActionEvent ev) {
Appointment a = new Appointment();
a.setWho(str);
insertAppointment(pt, a);
}
});
// Archive
insert.add(new AbstractAction("Arkivsaker") {
public void actionPerformed(ActionEvent ev) {
insertArchive(pt);
}
});
// Conflict
insert.add(new AbstractAction("Konflikt") {
public void actionPerformed(ActionEvent ev) {
insertConflict(pt);
}
});
return insert;
}
public JToolBar createToolBar() {
JToolBar toolbar = new JToolBar();
toolbar.setFloatable(true);
// Undo
toolbar.addSeparator();
URL undoUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/undo.gif");
ImageIcon undoIcon = new ImageIcon(undoUrl);
undo = new AbstractAction("", undoIcon) {
public void actionPerformed(ActionEvent e) {
undo();
}
};
undo.setEnabled(false);
toolbar.add(undo).setToolTipText("Angre");
// Redo
URL redoUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/redo.gif");
ImageIcon redoIcon = new ImageIcon(redoUrl);
redo = new AbstractAction("", redoIcon) {
public void actionPerformed(ActionEvent e) {
redo();
}
};
redo.setEnabled(false);
toolbar.add(redo).setToolTipText("Utfør likevel");
//
// Edit Block
//
toolbar.addSeparator();
Action action;
URL url;
// Copy
action = TransferHandler.getCopyAction();
url = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/copy.gif");
action.putValue(Action.SMALL_ICON, new ImageIcon(url));
toolbar.add(copy = new EventRedirector(action)).setToolTipText("Kopier");
// Paste
action = TransferHandler.getPasteAction();
url = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/paste.gif");
action.putValue(Action.SMALL_ICON, new ImageIcon(url));
toolbar.add(paste = new EventRedirector(action)).setToolTipText("Lim inn");
// Cut
action = TransferHandler.getCutAction();
url = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/cut.gif");
action.putValue(Action.SMALL_ICON, new ImageIcon(url));
toolbar.add(cut = new EventRedirector(action)).setToolTipText("Klipp ut");
// Remove
URL removeUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/delete.gif");
ImageIcon removeIcon = new ImageIcon(removeUrl);
remove = new AbstractAction("", removeIcon) {
public void actionPerformed(ActionEvent e) {
if (!graph.isSelectionEmpty()) {
Object[] cells = graph.getSelectionCells();
cells = graph.getDescendants(cells);
graph.getModel().remove(cells);
}
}
};
remove.setEnabled(false);
toolbar.add(remove).setToolTipText("Slett");
// Zoom Std
toolbar.addSeparator();
URL zoomUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/zoom100.gif");
ImageIcon zoomIcon = new ImageIcon(zoomUrl);
toolbar.add(new AbstractAction("", zoomIcon) {
public void actionPerformed(ActionEvent e) {
graph.setScale(1.0);
}
}).setToolTipText("Standard zoom");
// Zoom In
URL zoomInUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/zoomin.gif");
ImageIcon zoomInIcon = new ImageIcon(zoomInUrl);
toolbar.add(new AbstractAction("", zoomInIcon) {
public void actionPerformed(ActionEvent e) {
graph.setScale(1.2*graph.getScale());
}
}).setToolTipText("Zoom inn");
// Zoom Out
URL zoomOutUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/zoomout.gif");
ImageIcon zoomOutIcon = new ImageIcon(zoomOutUrl);
toolbar.add(new AbstractAction("", zoomOutIcon) {
public void actionPerformed(ActionEvent e) {
graph.setScale(graph.getScale()/1.2);
}
}).setToolTipText("Zoom ut");
// Group
//toolbar.addSeparator();
URL groupUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/group.gif");
ImageIcon groupIcon = new ImageIcon(groupUrl);
group = new AbstractAction("", groupIcon) {
public void actionPerformed(ActionEvent e) {
group(graph.getSelectionCells());
}
};
group.setEnabled(false);
//toolbar.add(group);
// XMLSAVE
URL xmlSaveUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/ungroup.gif");
ImageIcon xmlSaveIcon = new ImageIcon(xmlSaveUrl);
xmlSave = new AbstractAction("", xmlSaveIcon) {
public void actionPerformed(ActionEvent e) {
saveAsXML();
}
};
//toolbar.add(xmlSave).setToolTipText("Lagre som XML");
// XML Load
URL xmlLoadUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/ungroup.gif");
ImageIcon xmlLoadIcon = new ImageIcon(xmlLoadUrl);
xmlLoad = new AbstractAction("", xmlLoadIcon) {
public void actionPerformed(ActionEvent e) {
loadXML();
}
};
//toolbar.add(xmlLoad).setToolTipText("Åpne XML");
// XML Load
toolbar.addSeparator();
URL printUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/print.gif");
ImageIcon printIcon = new ImageIcon(printUrl);
print = new AbstractAction("", printIcon) {
public void actionPerformed(ActionEvent e) {
printGraph();
}
};
toolbar.add(print).setToolTipText("Skriv ut skisse");
toolbar.addSeparator();
URL feedbackUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/feedback.png");
ImageIcon feedbackIcon = new ImageIcon(feedbackUrl);
feedback = new AbstractAction("", feedbackIcon) {
public void actionPerformed(ActionEvent e) {
sendFeedback();
}
};
toolbar.add(feedback).setToolTipText("Send tilbakeMelding");
// Ungroup
URL ungroupUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/ungroup.gif");
ImageIcon ungroupIcon = new ImageIcon(ungroupUrl);
ungroup = new AbstractAction("", ungroupIcon) {
public void actionPerformed(ActionEvent e) {
ungroup(graph.getSelectionCells());
}
};
ungroup.setEnabled(false);
//toolbar.add(ungroup);
// To Front
toolbar.addSeparator();
URL toFrontUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/tofront.gif");
ImageIcon toFrontIcon = new ImageIcon(toFrontUrl);
tofront = new AbstractAction("", toFrontIcon) {
public void actionPerformed(ActionEvent e) {
if (!graph.isSelectionEmpty())
toFront(graph.getSelectionCells());
}
};
tofront.setEnabled(false);
//toolbar.add(tofront);
// To Back
URL toBackUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/toback.gif");
ImageIcon toBackIcon = new ImageIcon(toBackUrl);
toback = new AbstractAction("", toBackIcon) {
public void actionPerformed(ActionEvent e) {
if (!graph.isSelectionEmpty())
toBack(graph.getSelectionCells());
}
};
toback.setEnabled(false);
//toolbar.add(toback);
return toolbar;
}
public JToolBar createInsertBar() {
JToolBar toolbar = new JToolBar();
toolbar.setFloatable(true);
// Select
URL selectUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/selector.gif");
ImageIcon selectIcon = new ImageIcon(selectUrl);
selectButton = new JToggleButton(new AbstractAction("", selectIcon) {
public void actionPerformed(ActionEvent e) {
//
}
});
selectButton.setToolTipText("Velg");
buttonGroup.add(selectButton);
selectButton.setSelected(true);
toolbar.add(selectButton);
// Person
URL personUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/person.png");
ImageIcon personIcon = new ImageIcon(personUrl);
personButton = new JToggleButton(new AbstractAction("", personIcon) {
public void actionPerformed(ActionEvent e) {
//
}
});
personButton.setToolTipText("Legg til person");
buttonGroup.add(personButton);
toolbar.add(personButton);
// Note
URL noteUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/NewNote16.gif");
ImageIcon noteIcon = new ImageIcon(noteUrl);
noteButton = new JToggleButton(new AbstractAction("", noteIcon) {
public void actionPerformed(ActionEvent e) {
//
}
});
noteButton.setToolTipText("Legg til notat");
buttonGroup.add(noteButton);
toolbar.add(noteButton);
// Quote
URL quoteUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/quote.png");
ImageIcon quoteIcon = new ImageIcon(quoteUrl);
quoteButton = new JToggleButton(new AbstractAction("", quoteIcon) {
public void actionPerformed(ActionEvent e) {
//
}
});
quoteButton.setToolTipText("Legg til sitat");
buttonGroup.add(quoteButton);
toolbar.add(quoteButton);
// Event
URL eventUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/NewNote16.gif");
ImageIcon eventIcon = new ImageIcon(eventUrl);
eventButton = new JToggleButton(new AbstractAction("", eventIcon) {
public void actionPerformed(ActionEvent e) {
//
}
});
eventButton.setToolTipText("Legg inn ny hendelse");
buttonGroup.add(eventButton);
//toolbar.add(eventButton);
// Archive
URL archiveUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/archive.png");
ImageIcon archiveIcon = new ImageIcon(archiveUrl);
archiveButton = new JToggleButton(new AbstractAction("", archiveIcon) {
public void actionPerformed(ActionEvent e) {
//
}
});
archiveButton.setToolTipText("Legg til arkivsak");
buttonGroup.add(archiveButton);
toolbar.add(archiveButton);
// Appointment
URL appointmentUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/appointment.png");
ImageIcon appointmentIcon = new ImageIcon(appointmentUrl);
appointmentButton = new JToggleButton(new AbstractAction("", appointmentIcon) {
public void actionPerformed(ActionEvent e) {
//
}
});
appointmentButton.setToolTipText("Legg til avtale");
buttonGroup.add(appointmentButton);
toolbar.add(appointmentButton);
// Conflict
URL conflictUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/conflict.png");
ImageIcon conflictIcon = new ImageIcon(conflictUrl);
conflictButton = new JToggleButton(new AbstractAction("", conflictIcon) {
public void actionPerformed(ActionEvent e) {
//
}
});
conflictButton.setToolTipText("Legg til en konflikt/debatt");
buttonGroup.add(conflictButton);
toolbar.add(conflictButton);
// Place
URL placeUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/NewNote16.gif");
ImageIcon placeIcon = new ImageIcon(placeUrl);
placeButton = new JToggleButton(new AbstractAction("", placeIcon) {
public void actionPerformed(ActionEvent e) {
//
}
});
placeButton.setToolTipText("Legg inn et sted");
buttonGroup.add(placeButton);
//toolbar.add(placeButton);
toolbar.addSeparator();
// Edge
URL edgeUrl = getClass().getClassLoader().getResource("images/org/pegadi/storysketch/edge.gif");
ImageIcon edgeIcon = new ImageIcon(edgeUrl);
edgeButton = new JToggleButton(new AbstractAction("", edgeIcon) {
public void actionPerformed(ActionEvent e) {
}
});
edgeButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
updatePortsVisible();
}
});
edgeButton.setToolTipText("Lag relasjon (sammenkobling)");
buttonGroup.add(edgeButton);
toolbar.add(edgeButton);
return toolbar;
}
public void updatePortsVisible() {
graph.setPortsVisible(edgeButton.isSelected());
}
// This will change the source of the actionevent to graph.
protected class EventRedirector extends AbstractAction {
protected Action action;
// Construct the "Wrapper" Action
public EventRedirector(Action a) {
super("", (ImageIcon) a.getValue(Action.SMALL_ICON));
this.action = a;
}
// Redirect the Actionevent
public void actionPerformed(ActionEvent e) {
e = new ActionEvent(graph, e.getID(),
e.getActionCommand(), e.getModifiers());
action.actionPerformed(e);
}
}
// Update Undo/Redo Button State based on Undo Manager
protected void updateHistoryButtons() {
// The View Argument Defines the Context
undo.setEnabled(undoManager.canUndo(graph.getGraphLayoutCache()));
redo.setEnabled(undoManager.canRedo(graph.getGraphLayoutCache()));
}
// Insert a new Vertex at point
public void insert(Point point) {
// Construct Vertex with no Label
DefaultGraphCell vertex = new DefaultGraphCell();
// Add one Floating Port
vertex.add(new DefaultPort());
// Snap the Point to the Grid
point = graph.snap(new Point(point));
// Default Size for the new Vertex
Dimension size = new Dimension(50,60);
// Create a Map that holds the attributes for the Vertex
Map map = GraphConstants.createMap();
// Add a Bounds Attribute to the Map
GraphConstants.setBounds(map, new Rectangle(point, size));
// Add a Border Color Attribute to the Map
GraphConstants.setBorderColor(map, Color.black);
// Add a White Background
GraphConstants.setBackground(map, Color.white);
// Make Vertex Opaque
GraphConstants.setOpaque(map, true);
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Associate the Vertex with its Attributes
attributes.put(vertex, map);
// Insert the Vertex and its Attributes
graph.getModel().insert(new Object[]{vertex},
attributes,
null,
null,
null);
}
// Insert a new Vertex at point
public void insertPerson(Point point) {
insertPerson(point, null);
}
public void insertPerson(Point point, Person person) {
// Construct Vertex with no Label
PersonCell vertex;
if(person == null) {
vertex = new PersonCell();
} else {
vertex = new PersonCell(person);
}
// Add one Floating Port
vertex.add(new DefaultPort());
// Snap the Point to the Grid
point = graph.snap(new Point(point));
// Default Size for the new Vertex
Dimension size = new Dimension(55,70);
// Create a Map that holds the attributes for the Vertex
Map map = GraphConstants.createMap();
// Add a Bounds Attribute to the Map
//point.translate(-(int)size.getWidth()/2, -(int)size.getHeight()/2);
GraphConstants.setBounds(map, new Rectangle(point, size));
// Add a Border Color Attribute to the Map
//GraphConstants.setBorderColor(map, Color.black);
// Add a White Background
//GraphConstants.setBackground(map, Color.white);
// Make Vertex Opaque
//GraphConstants.setOpaque(map, true);
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Associate the Vertex with its Attributes
attributes.put(vertex, map);
// Insert the Vertex and its Attributes
graph.getModel().insert(new Object[]{vertex},
attributes,
null,
null,
null);
}
// Insert a new Vertex at point
public void insertNote(Point point) {
insertNote(point, null);
}
// Insert a new Vertex at point
public void insertNote(Point point, Note note) {
// Construct Vertex with no Label
NoteCell vertex;
if(note == null) {
vertex = new NoteCell();
} else {
vertex = new NoteCell(note);
}
// Add one Floating Port
vertex.add(new DefaultPort());
// Snap the Point to the Grid
point = graph.snap(new Point(point));
// Default Size for the new Vertex
Dimension size = new Dimension(50,65);
// Create a Map that holds the attributes for the Vertex
Map map = GraphConstants.createMap();
// Add a Bounds Attribute to the Map
GraphConstants.setBounds(map, new Rectangle(point, size));
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Associate the Vertex with its Attributes
attributes.put(vertex, map);
// Insert the Vertex and its Attributes
graph.getModel().insert(new Object[]{vertex},
attributes,
null,
null,
null);
}
// Insert a new Vertex at point
public void insertQuote(Point point) {
insertQuote(point, null);
}
// Insert a new Vertex at point
public void insertQuote(Point point, Quote quote) {
// Construct Vertex with no Label
QuoteCell vertex;
if(quote == null) {
vertex = new QuoteCell();
} else {
vertex = new QuoteCell(quote);
}
// Add one Floating Port
vertex.add(new DefaultPort());
// Snap the Point to the Grid
point = graph.snap(new Point(point));
// Default Size for the new Vertex
Dimension size = new Dimension(70,55);
// Create a Map that holds the attributes for the Vertex
Map map = GraphConstants.createMap();
// Add a Bounds Attribute to the Map
//point.translate(-(int) (size.getWidth()/2), - (int) (size.getHeight()/2));
GraphConstants.setBounds(map, new Rectangle(point, size));
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Associate the Vertex with its Attributes
attributes.put(vertex, map);
// Insert the Vertex and its Attributes
graph.getModel().insert(new Object[]{vertex},
attributes,
null,
null,
null);
}
// Insert a new Vertex at point
public void insertEvent(Point point) {
// Construct Vertex with no Label
EventCell vertex = new EventCell();
// Add one Floating Port
vertex.add(new DefaultPort());
// Snap the Point to the Grid
point = graph.snap(new Point(point));
// Default Size for the new Vertex
Dimension size = new Dimension(50,65);
// Create a Map that holds the attributes for the Vertex
Map map = GraphConstants.createMap();
// Add a Bounds Attribute to the Map
GraphConstants.setBounds(map, new Rectangle(point, size));
// Add a Border Color Attribute to the Map
GraphConstants.setBorderColor(map, Color.black);
// Add a White Background
GraphConstants.setBackground(map, Color.decode("#E1FCAE"));
// Make Vertex Opaque
GraphConstants.setOpaque(map, true);
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Associate the Vertex with its Attributes
attributes.put(vertex, map);
// Insert the Vertex and its Attributes
graph.getModel().insert(new Object[]{vertex},
attributes,
null,
null,
null);
}
public void insertAppointment(Point point) {
insertAppointment(point, null);
}
// Insert a new Vertex at point
public void insertAppointment(Point point, Appointment a) {
// Construct Vertex with no Label
AppointmentCell vertex;
if(a == null) {
vertex = new AppointmentCell();
} else {
vertex = new AppointmentCell(a);
}
// Add one Floating Port
vertex.add(new DefaultPort());
// Snap the Point to the Grid
point = graph.snap(new Point(point));
// Default Size for the new Vertex
Dimension size = new Dimension(100,60);
// Create a Map that holds the attributes for the Vertex
Map map = GraphConstants.createMap();
// Add a Bounds Attribute to the Map
GraphConstants.setBounds(map, new Rectangle(point, size));
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Associate the Vertex with its Attributes
attributes.put(vertex, map);
// Insert the Vertex and its Attributes
graph.getModel().insert(new Object[]{vertex},
attributes,
null,
null,
null);
}
public void insertArchive(Point point) {
insertArchive(point, null);
}
// Insert a new Vertex at point
public void insertArchive(Point point, Archive archive) {
// Construct Vertex with no Label
ArchiveCell vertex;
if(archive == null) {
vertex = new ArchiveCell();
} else {
vertex = new ArchiveCell(archive);
}
// Add one Floating Port
vertex.add(new DefaultPort());
// Snap the Point to the Grid
point = graph.snap(new Point(point));
// Default Size for the new Vertex
Dimension size = new Dimension(110,55);
// Create a Map that holds the attributes for the Vertex
Map map = GraphConstants.createMap();
// Add a Bounds Attribute to the Map
GraphConstants.setBounds(map, new Rectangle(point, size));
// Add a Border Color Attribute to the Map
GraphConstants.setBorderColor(map, Color.black);
// Add a White Background
GraphConstants.setBackground(map, Color.decode("#E1ACAE"));
// Make Vertex Opaque
GraphConstants.setOpaque(map, true);
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Associate the Vertex with its Attributes
attributes.put(vertex, map);
// Insert the Vertex and its Attributes
graph.getModel().insert(new Object[]{vertex},
attributes,
null,
null,
null);
}
public void insertConflict(Point point) {
insertConflict(point, null);
}
// Insert a new Vertex at point
public void insertConflict(Point point, Conflict conflict) {
java.util.List toInsert = new LinkedList();
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Construct Vertex with no Label
ConflictCell vertex;
if(conflict == null) {
vertex = new ConflictCell();
} else {
vertex = new ConflictCell(conflict);
}
// Add ports
DefaultPort leftPort = new DefaultPort();
toInsert.add(leftPort);
vertex.add(leftPort);
Map portMap = GraphConstants.createMap();
GraphConstants.setOffset(portMap,new Point(0,750));
attributes.put(leftPort,portMap);
DefaultPort rightPort = new DefaultPort();
toInsert.add(rightPort);
vertex.add(rightPort);
portMap = GraphConstants.createMap();
GraphConstants.setOffset(portMap,new Point(1000,750));
attributes.put(rightPort,portMap);
// Snap the Point to the Grid
point = graph.snap(new Point(point));
// Default Size for the new Vertex
Dimension size = new Dimension(65,70);
// Create a Map that holds the attributes for the Vertex
Map map = GraphConstants.createMap();
// Add a Bounds Attribute to the Map
GraphConstants.setBounds(map, new Rectangle(point, size));
// Add a Border Color Attribute to the Map
GraphConstants.setBorderColor(map, Color.black);
// Add a White Background
GraphConstants.setBackground(map, Color.decode("#ffffaa"));
// Make Vertex Opaque
GraphConstants.setOpaque(map, true);
toInsert.add(vertex);
// Associate the Vertex with its Attributes
attributes.put(vertex, map);
// Insert the Vertex and its Attributes
graph.getModel().insert(toInsert.toArray(),
attributes,
null,
null,
null);
}
// Insert a new Vertex at point
public void insertPlace(Point point) {
// Construct Vertex with no Label
PlaceCell vertex = new PlaceCell();
// Add one Floating Port
vertex.add(new DefaultPort());
// Snap the Point to the Grid
point = graph.snap(new Point(point));
// Default Size for the new Vertex
Dimension size = new Dimension(50,65);
// Create a Map that holds the attributes for the Vertex
Map map = GraphConstants.createMap();
// Add a Bounds Attribute to the Map
GraphConstants.setBounds(map, new Rectangle(point, size));
// Add a Border Color Attribute to the Map
GraphConstants.setBorderColor(map, Color.black);
// Add a White Background
GraphConstants.setBackground(map, Color.decode("#b2d9f7"));
// Make Vertex Opaque
GraphConstants.setOpaque(map, true);
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Associate the Vertex with its Attributes
attributes.put(vertex, map);
// Insert the Vertex and its Attributes
graph.getModel().insert(new Object[]{vertex},
attributes,
null,
null,
null);
}
// Insert a new Edge between source and target
public void connect(Port source, Port target) {
// Connections that will be inserted into the Model
ConnectionSet cs = new ConnectionSet();
// Construct Edge with no label
DefaultEdge edge = new DefaultEdge();
// Create Connection between source and target using edge
cs.connect(edge, source, target);
// Create a Map thath holds the attributes for the edge
Map map = GraphConstants.createMap();
// Add a Line End Attribute
//GraphConstants.setLineEnd(map, GraphConstants.ARROW_SIMPLE);
//Set style
GraphConstants.setRouting(map, GraphConstants.ROUTING_SIMPLE);
GraphConstants.setLineStyle(map, GraphConstants.STYLE_BEZIER);
// Construct a Map from cells to Maps (for insert)
Hashtable attributes = new Hashtable();
// Associate the Edge with its Attributes
attributes.put(edge, map);
// Insert the Edge and its Attributes
graph.getModel().insert(new Object[]{edge},
attributes,
cs,
null,
null);
}
// Create a Group that Contains the Cells
public void group(Object[] cells) {
// Order Cells by View Layering
cells = graph.getGraphLayoutCache().order(cells);
// If Any Cells in View
if (cells != null && cells.length > 0) {
// Create Group Cell
int count = getCellCount(graph);
DefaultGraphCell group = new DefaultGraphCell(count - 1);
// Create Change Information
ParentMap map = new ParentMap();
// Insert Child Parent Entries
for (Object cell : cells) map.addEntry(cell, group);
// Insert into model
graph.getModel().insert(new Object[]{group},
null,
null,
map,
null);
}
}
// Returns the total number of cells in a graph
protected int getCellCount(JGraph graph) {
Object[] cells = graph.getDescendants(graph.getRoots());
return cells.length;
}
public void loadXML() {
JFileChooser chooser = new JFileChooser();
int returnVal = chooser.showOpenDialog(this);
if(returnVal != JFileChooser.APPROVE_OPTION) {
return;
}
File file = chooser.getSelectedFile();
DOMParser parser = new DOMParser();
Document doc = null;
try {
parser.parse(file.getAbsolutePath());
} catch (SAXException se) {
log.error("Error parsing Story Sketch", se);
} catch (java.io.IOException ioe) {
log.error("Error parsing Story Sketch", ioe);
}
GraphModel newModel = XMLUtil.getGraphModel(parser.getDocument().getDocumentElement());
setModel(newModel);
}
public void saveAsXML() {
JFileChooser chooser = new JFileChooser();
int returnVal = chooser.showSaveDialog(this);
if(returnVal != JFileChooser.APPROVE_OPTION) {
return;
}
File file = chooser.getSelectedFile();
DocumentBuilderFactory fac;
DocumentBuilder db = null;
Document doc;
try {
fac = DocumentBuilderFactory.newInstance();
db = fac.newDocumentBuilder();
} catch (FactoryConfigurationError e) {
log.error("Error saving", e);
} catch (ParserConfigurationException e) {
log.error("Error saving", e);
}
doc = db.newDocument();
Element root = doc.createElement("storysketch");
doc.appendChild(root);
root.appendChild(XMLUtil.createModelElement(getModel(), doc));
SerializerFactory sFac =
SerializerFactory.getSerializerFactory("xml");
OutputFormat format = new
OutputFormat(doc, "iso-8859-1", true);
format.setLineWidth(0); // To disable line wrapping.
DOMSerializer ser;
try {
ser = sFac.makeSerializer(new java.io.FileOutputStream(file.getAbsolutePath()), format).asDOMSerializer();
ser.serialize(doc);
} catch (Exception e) {
e.printStackTrace();
}
}
// Ungroup the Groups in Cells and Select the Children
public void ungroup(Object[] cells) {
// If any Cells
if (cells != null && cells.length > 0) {
// List that Holds the Groups
ArrayList groups = new ArrayList();
// List that Holds the Children
ArrayList children = new ArrayList();
// Loop Cells
for (Object cell : cells) {
// If Cell is a Group
if (isGroup(cell)) {
// Add to List of Groups
groups.add(cell);
// Loop Children of Cell
for (int j = 0; j < graph.getModel().getChildCount(cell); j++) {
// Get Child from Model
Object child = graph.getModel().getChild(cell, j);
// If Not Port
if (!(child instanceof Port))
// Add to Children List
children.add(child);
}
}
}
// Remove Groups from Model (Without Children)
graph.getModel().remove(groups.toArray());
// Select Children
graph.setSelectionCells(children.toArray());
}
}
// Determines if a Cell is a Group
public boolean isGroup(Object cell) {
// Map the Cell to its View
CellView view = graph.getGraphLayoutCache().getMapping(cell, false);
return view != null && !view.isLeaf();
}
// Brings the Specified Cells to Front
public void toFront(Object[] c) {
graph.getGraphLayoutCache().toFront(c);
}
// Sends the Specified Cells to Back
public void toBack(Object[] c) {
graph.getGraphLayoutCache().toBack(c);
}
// Undo the last Change to the Model or the View
public void undo() {
try {
undoManager.undo(graph.getGraphLayoutCache());
} catch (Exception ex) {
log.error("Error undoing", ex);
} finally {
updateHistoryButtons();
}
}
// Redo the last Change to the Model or the View
public void redo() {
try {
undoManager.redo(graph.getGraphLayoutCache());
} catch (Exception ex) {
log.error("Error redoing", ex);
} finally {
updateHistoryButtons();
}
}
// From GraphSelectionListener Interface
public void valueChanged(GraphSelectionEvent e) {
// Group Button only Enabled if more than One Cell Selected
group.setEnabled(graph.getSelectionCount() > 1);
// Update Button States based on Current Selection
boolean enabled = !graph.isSelectionEmpty();
remove.setEnabled(enabled); ungroup.setEnabled(enabled);
tofront.setEnabled(enabled); toback.setEnabled(enabled);
copy.setEnabled(enabled); cut.setEnabled(enabled);
}
//
// KeyListener for Delete KeyStroke
//
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }
public void keyPressed(KeyEvent e) {
// Listen for Delete Key Press
if (e.getKeyCode() == KeyEvent.VK_DELETE)
// Execute Remove Action on Delete Key Press
remove.actionPerformed(null);
}
public void info_hyperlinkUpdate(HyperlinkEvent e) {
if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
log.info("Link is: " + e.getURL());
if(e.getURL().toString().startsWith("mailto:")) {
application.sendMail(e.getURL().toString().substring(7), "", "");
}
else if (e.getURL().toString().startsWith("http://underdusken.no:8888")) {
try {
info.setPage(e.getURL());
} catch (Exception ex) {
JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(info), "Kunne ikke laste side",
"Feil ved lasting",
JOptionPane.ERROR_MESSAGE);
ex.printStackTrace();
}
} else {
log.info("Launching URL in browser");
if(launcher != null)
launcher.browserLaunch(e.getURL().toString());
}
}
}
public void sendFeedback() {
application.sendMail("bjorsnos@underdusken.no", "Tilbakemelding ang. saksskisse", "Hei, \n\nSaksskissa er veldig bra, men:");
}
public void printGraph() {
PrinterJob printJob = PrinterJob.getPrinterJob();
PageFormat format = printJob.defaultPage();
Paper paper = new Paper();
log.info("Default size is : {}/{}", paper.getWidth(), paper.getHeight());
double A4_WIDTH = (210/25.4)*72;
double A4_HEIGHT = (297/25.4)*72;
double MARGIN = (10/25.4)*72;
paper.setSize(A4_WIDTH, A4_HEIGHT);
paper.setImageableArea(MARGIN, MARGIN, A4_WIDTH - MARGIN, A4_HEIGHT - MARGIN);
log.info("New size is : {}/{}", paper.getWidth(), paper.getHeight());
format.setPaper(paper);
Dimension d = graph.getPreferredSize();
double d1 = (d.getHeight()/graph.getScale()) / (d.getWidth()/graph.getScale());
double d2 = format.getImageableHeight()/format.getImageableHeight();
if(d1 < d2 && (format.getImageableWidth() < (d.getWidth()/graph.getScale()) || format.getImageableHeight() < (d.getHeight()/graph.getScale()))) {
log.info("setting landscape orientation");
format.setOrientation(PageFormat.LANDSCAPE);
}
printJob.setPrintable(this, format);
if (printJob.printDialog()) {
try {
printJob.print();
} catch (Exception printException) {
printException.printStackTrace();
}
}
}
public int print(Graphics g, PageFormat format, int page) {
if(page > 0) {
return NO_SUCH_PAGE;
} else {
Dimension d = graph.getPreferredSize();
double d1 = d.getHeight()/(d.getWidth()/graph.getScale());
double d2 = format.getImageableHeight()/ format.getImageableHeight();
double sx = format.getImageableWidth() / (d.getWidth()/graph.getScale()) ;
double sy = format.getImageableHeight() / (d.getHeight()/graph.getScale());
double scale = Math.min(sx, sy);
if(scale > 1)
scale = 1;
g.translate((int)format.getImageableX(), (int)format.getImageableY());
StorySketchGraph printGraph = new StorySketchGraph(graph.getModel());
printGraph.setScale(scale);
JScrollPane p = new JScrollPane(printGraph);
JFrame f = new JFrame();
f.getContentPane().add(p);
f.pack();
printGraph.paint(g);
String label = "Saksskisse skrevet ut av Pegadi - " +
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(new Date());
FontMetrics fm = g.getFontMetrics();
int lw = fm.stringWidth(label);
g.drawString(label,
(int) (format.getImageableWidth()-lw),
//0+fm.getHeight());
(int) (format.getImageableHeight() -format.getImageableY() -fm.getMaxDescent()));
return PAGE_EXISTS;
}
}
public void setStorySketchApplication(StorySketchApplication application) {
this.application = application;
}
public class StorySketchGraph extends JGraph {
public StorySketchGraph(GraphModel model) {
super(model);
setMarqueeHandler(new StorySketchMarqueeHandler());
}
/**
* Override parent method with custom GraphUI.
*/
public void updateUI() {
setUI(new EditorGraphUI());
invalidate();
}
// Overrides JGraph.createVertexView
protected VertexView createVertexView(Object v, CellMapper cm) {
if (v instanceof PersonCell) {
return new PersonView(v, this, cm);
} else if (v instanceof NoteCell) {
return new NoteView(v, this, cm);
} else if (v instanceof QuoteCell) {
return new QuoteView(v, this, cm);
} else if (v instanceof EventCell) {
return new EventView(v, this, cm);
} else if (v instanceof AppointmentCell) {
return new AppointmentView(v, this, cm);
} else if (v instanceof ArchiveCell) {
return new ArchiveView(v, this, cm);
} else if (v instanceof ConflictCell) {
return new ConflictView(v, this, cm);
} else if (v instanceof PlaceCell) {
return new PlaceView(v, this, cm);
}
// Else Call Superclass
return super.createVertexView(v,cm);
}
// Override Superclass Method to Return Custom EdgeView
protected EdgeView createEdgeView(Object e, CellMapper cm) {
// Return Custom EdgeView
return new EdgeView(e, this, cm) {
// Override Superclass Method
public boolean isAddPointEvent(MouseEvent event) {
// Points are Added using Shift-Click
return event.isShiftDown();
}
// Override Superclass Method
public boolean isRemovePointEvent(MouseEvent event) {
// Points are Removed using Shift-Click
return event.isShiftDown();
}
};
}
}
public class StorySketchMarqueeHandler extends BasicMarqueeHandler {
// Holds the Start and the Current Point
protected Point start, current;
// Holds the First and the Current Port
protected PortView port, firstPort;
// Override to Gain Control (for PopupMenu and ConnectMode)
public boolean isForceMarqueeEvent(MouseEvent e) {
// If Right Mouse Button we want to Display the PopupMenu
if (SwingUtilities.isRightMouseButton(e))
// Return Immediately
return true;
// Find and Remember Port
port = getSourcePortAt(e.getPoint());
// If Port Found and in ConnectMode (=Ports Visible)
if (port != null && graph.isPortsVisible())
return true;
// Else Call Superclass
return super.isForceMarqueeEvent(e);
}
// Display PopupMenu or Remember Start Location and First Port
public void mousePressed(final MouseEvent e) {
Point loc = graph.fromScreen(e.getPoint());
// If Right Mouse Button
if (SwingUtilities.isRightMouseButton(e)) {
// Scale From Screen to Model
// Find Cell in Model Coordinates
Object cell = graph.getFirstCellForLocation(e.getPoint().x, e.getPoint().y);
// Create PopupMenu for the Cell
JPopupMenu menu = createPopupMenu(e, cell);
// Display PopupMenu
menu.show(graph, e.getX(), e.getY());
e.consume();
// Else if in ConnectMode and Remembered Port is Valid
} else if (personButton.isSelected()) {
insertPerson(loc);
selectButton.setSelected(true);
e.consume();
} else if (noteButton.isSelected()) {
insertNote(loc);
selectButton.setSelected(true);
e.consume();
} else if (quoteButton.isSelected()) {
insertQuote(loc);
selectButton.setSelected(true);
e.consume();
} else if (eventButton.isSelected()) {
insertEvent(loc);
selectButton.setSelected(true);
e.consume();
} else if (appointmentButton.isSelected()) {
insertAppointment(loc);
selectButton.setSelected(true);
e.consume();
} else if (archiveButton.isSelected()) {
insertArchive(loc);
selectButton.setSelected(true);
e.consume();
} else if (conflictButton.isSelected()) {
insertConflict(loc);
selectButton.setSelected(true);
e.consume();
} else if (placeButton.isSelected()) {
insertPlace(loc);
selectButton.setSelected(true);
e.consume();
} else if (port != null && !e.isConsumed() && graph.isPortsVisible()) {
// Remember Start Location
start = graph.toScreen(port.getLocation(null));
// Remember First Port
firstPort = port;
// Consume Event
e.consume();
} else {
// Call Superclass
super.mousePressed(e);
}
}
// Find Port under Mouse and Repaint Connector
public void mouseDragged(MouseEvent e) {
// If remembered Start Point is Valid
if (start != null && !e.isConsumed()) {
// Fetch Graphics from Graph
Graphics g = graph.getGraphics();
// Xor-Paint the old Connector (Hide old Connector)
paintConnector(Color.black, graph.getBackground(), g);
// Reset Remembered Port
port = getTargetPortAt(e.getPoint());
// If Port was found then Point to Port Location
if (port != null) current = graph.toScreen(port.getLocation(null));
// Else If no Port was found then Point to Mouse Location
else current = graph.snap(e.getPoint());
// Xor-Paint the new Connector
paintConnector(graph.getBackground(), Color.black, g);
// Consume Event
e.consume();
}
// Call Superclass
super.mouseDragged(e);
}
public PortView getSourcePortAt(Point point) {
// Scale from Screen to Model
Point tmp = graph.fromScreen(new Point(point));
// Find a Port View in Model Coordinates and Remember
return graph.getPortViewAt(tmp.x, tmp.y);
}
// Find a Cell at point and Return its first Port as a PortView
protected PortView getTargetPortAt(Point point) {
// Find Cell at point (No scaling needed here)
Object cell = graph.getFirstCellForLocation(point.x, point.y);
// Loop Children to find PortView
for (int i = 0; i < graph.getModel().getChildCount(cell); i++) {
// Get Child from Model
Object tmp = graph.getModel().getChild(cell, i);
// Get View for Child using the Graph's View as a Cell Mapper
tmp = graph.getGraphLayoutCache().getMapping(tmp, false);
// If Child View is a Port View and not equal to First Port
if (tmp instanceof PortView && tmp != firstPort)
// Return as PortView
return (PortView) tmp;
}
// No Port View found
return getSourcePortAt(point);
}
// Connect the First Port and the Current Port in the Graph or Repaint
public void mouseReleased(MouseEvent e) {
// If Valid Event, Current and First Port
if (e != null && !e.isConsumed() && port != null && firstPort != null &&
firstPort != port) {
// Then Establish Connection
connect((Port) firstPort.getCell(), (Port) port.getCell());
selectButton.setSelected(true);
// Consume Event
e.consume();
// Else Repaint the Graph
} else graph.repaint();
// Reset Global Vars
firstPort = port = null;
start = current = null;
// Call Superclass
super.mouseReleased(e);
}
// Show Special Cursor if Over Port
public void mouseMoved(MouseEvent e) {
// Check Mode and Find Port
if (e != null && getSourcePortAt(e.getPoint()) != null &&
!e.isConsumed() && graph.isPortsVisible()) {
// Set Cusor on Graph (Automatically Reset)
graph.setCursor(new Cursor(Cursor.HAND_CURSOR));
// Consume Event
e.consume();
}
super.mouseMoved(e);
}
// Use Xor-Mode on Graphics to Paint Connector
protected void paintConnector(Color fg, Color bg, Graphics g) {
// Set Foreground
g.setColor(fg);
// Set Xor-Mode Color
g.setXORMode(bg);
// Highlight the Current Port
paintPort(graph.getGraphics());
// If Valid First Port, Start and Current Point
if (firstPort != null && start != null && current != null)
// Then Draw A Line From Start to Current Point
g.drawLine(start.x, start.y, current.x, current.y);
}
// Use the Preview Flag to Draw a Highlighted Port
protected void paintPort(Graphics g) {
// If Current Port is Valid
if (port != null) {
// If Not Floating Port...
boolean o = (GraphConstants.getOffset(port.getAttributes()) != null);
// ...Then use Parent's Bounds
Rectangle r = (o) ? port.getBounds() : port.getParentView().getBounds();
// Scale from Model to Screen
r = graph.toScreen(new Rectangle(r));
// Add Space For the Highlight Border
r.setBounds(r.x-3, r.y-3, r.width+6, r.height+6);
// Paint Port in Preview (=Highlight) Mode
graph.getUI().paintCell(g, port, r, true);
}
}
}
public class StorySketchTransferHandler extends TransferHandler {
public StorySketchTransferHandler() {
}
public boolean canImport(JComponent c, DataFlavor[] flavors) {
for (DataFlavor flavor : flavors) {
if (DataFlavor.stringFlavor.equals(flavor)) {
return true;
}
}
return false;
}
public boolean importData(JComponent c, Transferable t) {
if (canImport(c, t.getTransferDataFlavors())) {
try {
String str = (String)t.getTransferData(DataFlavor.stringFlavor);
log.info("Transfered {}", str);
return true;
} catch (UnsupportedFlavorException ignored) {
} catch (IOException ignored) {
}
}
return false;
}
}
class OverviewPanel extends JPanel implements ComponentListener, GraphModelListener, MouseListener, MouseMotionListener, ChangeListener, PropertyChangeListener {
//PannerViewfinder v;
Rectangle r;
JGraph graph;
JGraph originalGraph;
JScrollPane scroll;
double scale = 1;
Point last;
Rectangle rect = new Rectangle();
double zoom = 1;
public OverviewPanel(JGraph g, JScrollPane scroll) {
originalGraph = g;
this.scroll=scroll;
graph = new StorySketchGraph(g.getModel());
graph.setAntiAliased(true);
graph.setEnabled(false);
setLayout(new BorderLayout());
add(graph, BorderLayout.CENTER);
g.getModel().addGraphModelListener(this);
addComponentListener(this);
graph.addMouseListener(this);
graph.addMouseMotionListener(this);
scroll.getViewport().addChangeListener(this);
g.addPropertyChangeListener(JGraph.SCALE_PROPERTY, this);
}
public void propertyChange(PropertyChangeEvent evt) {
zoom = (Double) evt.getNewValue();
componentResized(null);
}
public GraphModel getModel() {
return graph.getModel();
}
public void setModel(GraphModel model) {
graph.setModel(model);
}
public void graphChanged(GraphModelEvent e) {
componentResized(null);
repaint();
}
public void stateChanged(ChangeEvent e) {
updatePannerFromView();
repaint();
}
public void componentResized(ComponentEvent e) {
Dimension d = scroll.getViewport().getView().getSize();
Dimension s = getSize();
double sx = s.getWidth() / d.getWidth();
double sy = s.getHeight() / d.getHeight();
scale = Math.min(sx, sy);
graph.setScale(scale*zoom);
updatePannerFromView();
repaint();
}
public void componentShown(ComponentEvent e) {
componentResized(e);
}
public void componentHidden(ComponentEvent e) {
}
public void componentMoved(ComponentEvent e) {
}
public void mousePressed(MouseEvent e) {
last = e.getPoint();
}
public void mouseMoved(MouseEvent e) {
if(rect.contains(e.getPoint())) {
setCursor(new Cursor(Cursor.HAND_CURSOR));
} else {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
}
public void mouseDragged(MouseEvent e) {
//updatePanner(e);
updateViewport(e);
updatePannerFromView();
repaint();
}
public void mouseReleased(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
public void mouseEntered(MouseEvent e) {
}
public void updatePannerFromView() {
Rectangle r = scroll.getViewport().getViewRect();
rect = new Rectangle((int) (r.getX() * scale),
(int) (r.getY() * scale),
(int) (r.getWidth() * scale)-1,
(int) (r.getHeight() * scale)-1);
}
public void updatePanner(MouseEvent e) {
int tx = (int) (e.getX() - last.getX());
int ty = (int) (e.getY() - last.getY());
Rectangle newRect = new Rectangle(rect);
newRect.translate(tx,ty);
int x1 = (int) newRect.getX();
int y1 = (int) newRect.getY();
int x2 = x1 + (int) newRect.getWidth();
int y2 = y1 + (int) newRect.getHeight();
if(x1 >= 0
&& y1 >= 0
&& x2 <= getSize().getWidth()
&& y2 < getSize().getHeight()) {
rect = newRect;
}
last = e.getPoint();
}
public void updateViewport(MouseEvent e) {
double dx = e.getX() - last.getX();
double dy = e.getY() - last.getY();
double dxV = dx/scale;
double dyV = dy/scale;
Rectangle r = scroll.getViewport().getViewRect();
int x = (int) (r.getLocation().getX() + dxV);
int y = (int) (r.getLocation().getY() + dyV);
Dimension d = scroll.getViewport().getViewSize();
if(x + r.getWidth() > d.getWidth())
x = (int) (d.getWidth() -r.getWidth());
if(y + r.getHeight() > d.getHeight())
y = (int) (d.getHeight() -r.getHeight());
if(x < 0) {
x=0;
}
if(y < 0) {
y=0;
}
Point p = new Point(x,y);
p.setLocation(x, y);
scroll.getViewport().setViewPosition(p);
last = e.getPoint();
}
public void paintChildren(Graphics g) {
super.paintChildren(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.RED);
g2.draw(rect);
}
}
/**
* Definition of the custom GraphUI.
*/
public class EditorGraphUI extends BasicGraphUI {
protected CellEditorListener cellEditorListener;
protected JFrame editDialog = null;
/**
* Create the dialog using the cell's editing component.
*/
protected void createEditDialog(Object cell, MouseEvent event) {
Dimension editorSize = editingComponent.getPreferredSize();
editDialog = new JFrame(graph.convertValueToString(cell));
editDialog.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
graph.stopEditing();
}
});
editDialog.getContentPane().setLayout(new GridLayout(1, 1));
editDialog.getContentPane().add(editingComponent);
Point p = null;
if(event != null) {
p = event.getPoint();
} else {
if(cell instanceof DefaultGraphCell) {
DefaultGraphCell c = (DefaultGraphCell) cell;
p = GraphConstants.getBounds(c.getAttributes()).getLocation();
}
}
SwingUtilities.convertPointToScreen(p, graph);
editingComponent.validate();
editDialog.pack();
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
if(p.getX() + editingComponent.getSize().getWidth() +50 > screen.getWidth()) {
p.translate(-(int) (p.getX() +editingComponent.getSize().getWidth() +50 -screen.getWidth()),0);
}
if(p.getY() + editingComponent.getSize().getHeight() +50 > screen.getHeight()) {
p.translate(0, -(int) (p.getY() +editingComponent.getSize().getHeight() +50 -screen.getHeight()));
}
editDialog.setLocation(p);
//editDialog.setResizable(false);
editDialog.setVisible(true);
}
protected void completeEditing() {
/* If should invoke stopCellEditing, try that */
if (graph.getInvokesStopCellEditing()
&& stopEditingInCompleteEditing
&& editingComponent != null) {
cellEditor.stopCellEditing();
}
/* Invoke cancelCellEditing, this will do nothing if stopCellEditing
* was successful.
*/
completeEditing(false, true, true);
}
/**
* Stops the editing session. If messageStop is true the editor
* is messaged with stopEditing, if messageCancel is true the
* editor is messaged with cancelEditing. If messageGraph is true
* the graphModel is messaged with valueForCellChanged.
*/
protected void completeEditing(boolean messageStop,
boolean messageCancel,
boolean messageGraph) {
if(editingCell instanceof Edge) {
super.completeEditing(messageStop, messageCancel, messageGraph);
return;
}
if (stopEditingInCompleteEditing
&& editingComponent != null
&& editDialog != null) {
Component oldComponent = editingComponent;
Object oldCell = editingCell;
GraphCellEditor oldEditor = cellEditor;
Object newValue = oldEditor.getCellEditorValue();
Rectangle editingBounds = graph.getCellBounds(editingCell);
boolean requestFocus =
((graph.hasFocus() || editingComponent.hasFocus()));
editingCell = null;
editingComponent = null;
if (messageStop)
oldEditor.stopCellEditing();
else if (messageCancel)
oldEditor.cancelCellEditing();
editDialog.dispose();
if (requestFocus)
graph.requestFocus();
if (messageGraph) {
Map map = GraphConstants.createMap();
GraphConstants.setValue(map, newValue);
Map nested = new Hashtable();
nested.put(oldCell, map);
graphLayoutCache.edit(nested, null, null, null);
}
updateSize();
// Remove Editor Listener
if (oldEditor != null && cellEditorListener != null)
oldEditor.removeCellEditorListener(cellEditorListener);
cellEditor = null;
editDialog = null;
}
}
/**
* Will start editing for cell if there is a cellEditor and
* shouldSelectCell returns true.<p>
* This assumes that cell is valid and visible.
*/
protected boolean startEditing(Object cell, MouseEvent event) {
if(cell instanceof Edge) {
return super.startEditing(cell, event);
}
completeEditing();
if (graph.isCellEditable(cell) && editDialog == null) {
// Create Editing Component **** *****
CellView tmp = graphLayoutCache.getMapping(cell, false);
cellEditor = tmp.getEditor();
editingComponent =
cellEditor.getGraphCellEditorComponent(
graph,
cell,
graph.isCellSelected(cell));
if (cellEditor.isCellEditable(event)) {
editingCell = cell;
// Create Wrapper Dialog **** *****
createEditDialog(cell, event);
// Add Editor Listener
if (cellEditorListener == null)
cellEditorListener = createCellEditorListener();
if (cellEditor != null && cellEditorListener != null)
cellEditor.addCellEditorListener(cellEditorListener);
if (cellEditor.shouldSelectCell(event)) {
stopEditingInCompleteEditing = false;
try {
graph.setSelectionCell(cell);
} catch (Exception e) {
log.error("Error setting selection", e);
}
stopEditingInCompleteEditing = true;
}
if (event != null) {
/* Find the component that will get forwarded all the
mouse events until mouseReleased. */
Point componentPoint =
SwingUtilities.convertPoint(
graph,
new Point(event.getX(), event.getY()),
editingComponent);
/* Create an instance of BasicTreeMouseListener to handle
passing the mouse/motion events to the necessary
component. */
// We really want similiar behavior to getMouseEventTarget,
// but it is package private.
Component activeComponent =
SwingUtilities.getDeepestComponentAt(
editingComponent,
componentPoint.x,
componentPoint.y);
if (activeComponent != null) {
new MouseInputHandler(
graph,
activeComponent,
event);
}
}
return true;
} else
editingComponent = null;
}
return false;
}
}
}