fiore@0: /* fiore@0: CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool fiore@0: fiore@0: Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com) fiore@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) fiore@0: fiore@0: This program is free software: you can redistribute it and/or modify fiore@0: it under the terms of the GNU General Public License as published by fiore@0: the Free Software Foundation, either version 3 of the License, or fiore@0: (at your option) any later version. fiore@0: fiore@0: This program is distributed in the hope that it will be useful, fiore@0: but WITHOUT ANY WARRANTY; without even the implied warranty of fiore@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fiore@0: GNU General Public License for more details. fiore@0: fiore@0: You should have received a copy of the GNU General Public License fiore@0: along with this program. If not, see . fiore@0: */ fiore@0: fiore@0: package uk.ac.qmul.eecs.ccmi.gui; fiore@0: fiore@0: import java.awt.Color; fiore@0: import java.awt.Component; fiore@0: import java.awt.Graphics; fiore@0: import java.awt.Graphics2D; fiore@0: import java.awt.event.ActionEvent; fiore@0: import java.awt.event.ActionListener; fiore@0: import java.awt.geom.AffineTransform; fiore@0: import java.awt.geom.Line2D; fiore@0: import java.awt.geom.Point2D; fiore@0: import java.awt.geom.Rectangle2D; fiore@0: import java.util.Enumeration; fiore@0: import java.util.ResourceBundle; fiore@0: fiore@0: import javax.swing.ButtonGroup; fiore@0: import javax.swing.Icon; fiore@0: import javax.swing.JButton; fiore@0: import javax.swing.JToggleButton; fiore@0: import javax.swing.JToolBar; fiore@0: fiore@0: fiore@0: /** fiore@0: A Toolbar that contains node and edge prototype icons. By using the toolbar fiore@0: the user can create nodes and edges in the diagram out of clonation from the prototype. fiore@0: */ fiore@0: @SuppressWarnings("serial") fiore@0: public class GraphToolbar extends JToolBar { fiore@5: /** fiore@5: * Constructs a tool bar with no icons. fiore@5: * fiore@5: * @param diagram the diagram this toolbar is related to fiore@0: */ fiore@0: public GraphToolbar(Diagram diagram){ fiore@0: /* creates icon for select button */ fiore@0: Icon icon = new Icon(){ fiore@0: public int getIconHeight() { return BUTTON_SIZE; } fiore@0: public int getIconWidth() { return BUTTON_SIZE; } fiore@0: public void paintIcon(Component c, Graphics g, fiore@0: int x, int y){ fiore@0: Graphics2D g2 = (Graphics2D)g; fiore@0: GraphPanel.drawGrabber(g2, x + OFFSET, y + OFFSET); fiore@0: GraphPanel.drawGrabber(g2, x + OFFSET, y + BUTTON_SIZE - OFFSET); fiore@0: GraphPanel.drawGrabber(g2, x + BUTTON_SIZE - OFFSET, y + OFFSET); fiore@0: GraphPanel.drawGrabber(g2, x + BUTTON_SIZE - OFFSET, y + BUTTON_SIZE - OFFSET); fiore@0: } fiore@0: }; fiore@0: /* add selection button */ fiore@0: ResourceBundle resources = fiore@0: ResourceBundle.getBundle(EditorFrame.class.getName()); fiore@0: String text = resources.getString("grabber.text"); fiore@0: selectButton = new NodeButton(null,icon); fiore@0: selectButton.setToolTipText(text); fiore@0: nodeButtonsGroup = new ButtonGroup(); fiore@0: nodeButtonsGroup.add(selectButton); fiore@0: add(selectButton); fiore@0: fiore@0: /* add diagram buttons to the toolbar */ fiore@0: Node[] nodeTypes = diagram.getNodePrototypes(); fiore@0: for (int i = 0; i < nodeTypes.length; i++){ fiore@0: text = nodeTypes[i].getType(); fiore@0: add(nodeTypes[i], text ); fiore@0: } fiore@0: fiore@0: /* select the select-button as default */ fiore@0: nodeButtonsGroup.setSelected(selectButton.getModel(), true); fiore@0: fiore@0: /* separate node buttons from edge buttons */ fiore@0: addSeparator(); fiore@0: fiore@0: /* add diagram edges to the toolbar */ fiore@0: Edge[] edgeTypes = diagram.getEdgePrototypes(); fiore@0: for (int i = 0; i < edgeTypes.length; i++){ fiore@0: text = edgeTypes[i].getType(); fiore@0: add(edgeTypes[i], text ); fiore@0: } fiore@0: } fiore@0: fiore@0: /** fiore@0: Gets the node prototype that is associated with fiore@0: the currently selected button fiore@5: @return a {@code Node} prototype fiore@0: */ fiore@0: public Node getSelectedTool() { fiore@0: @SuppressWarnings("rawtypes") fiore@0: Enumeration elements = nodeButtonsGroup.getElements(); fiore@0: while (elements.hasMoreElements()) { fiore@0: NodeButton b = (NodeButton)elements.nextElement(); fiore@0: if (b.isSelected()) { fiore@0: /* switch back to the select-button */ fiore@0: nodeButtonsGroup.setSelected(selectButton.getModel(), true); fiore@0: return b.getNode(); fiore@0: } fiore@0: } fiore@0: /* getting here means the selection button is selected */ fiore@0: return null; fiore@0: } fiore@0: fiore@0: /** fiore@0: Adds a node to the tool bar. fiore@0: @param n the node to add fiore@5: @param tip the tool tip appearing when hovering on this edge button fiore@0: */ fiore@5: public void add(final Node n, String tip){ fiore@0: Icon icon = new Icon(){ fiore@0: public int getIconHeight() { return BUTTON_SIZE; } fiore@0: public int getIconWidth() { return BUTTON_SIZE; } fiore@0: public void paintIcon(Component c, Graphics g, fiore@0: int x, int y){ fiore@0: double width = n.getBounds().getWidth(); fiore@0: double height = n.getBounds().getHeight(); fiore@0: Graphics2D g2 = (Graphics2D)g; fiore@0: double scaleX = (BUTTON_SIZE - OFFSET)/ width; fiore@0: double scaleY = (BUTTON_SIZE - OFFSET)/ height; fiore@0: double scale = Math.min(scaleX, scaleY); fiore@0: fiore@0: AffineTransform oldTransform = g2.getTransform(); fiore@0: g2.translate(x, y); fiore@0: g2.translate(OFFSET/2*scaleX,OFFSET/2*scaleY); fiore@0: g2.scale(scale, scale); fiore@0: g2.setColor(Color.black); fiore@0: n.draw(g2); fiore@0: g2.setTransform(oldTransform); fiore@0: } fiore@0: }; fiore@0: fiore@0: NodeButton button = new NodeButton(n, icon); fiore@5: button.setToolTipText(tip); fiore@0: fiore@0: add(button); fiore@0: nodeButtonsGroup.add(button); fiore@0: } fiore@0: fiore@0: /** fiore@0: Adds an edge to the tool bar. fiore@5: @param e the edge to add fiore@5: @param tip the tool tip appearing when hovering on this edge button fiore@0: */ fiore@5: public void add(final Edge e, String tip){ fiore@0: Icon icon = new Icon(){ fiore@0: public int getIconHeight() { return BUTTON_SIZE; } fiore@0: public int getIconWidth() { return BUTTON_SIZE; } fiore@0: public void paintIcon(Component c, Graphics g, fiore@0: int x, int y){ fiore@0: Graphics2D g2 = (Graphics2D)g; fiore@0: /* create two points */ fiore@0: Point2D p = new Point2D.Double(); fiore@0: Point2D q = new Point2D.Double(); fiore@0: p.setLocation(OFFSET, OFFSET); fiore@0: q.setLocation(BUTTON_SIZE - OFFSET, BUTTON_SIZE - OFFSET); fiore@0: fiore@0: Line2D line = new Line2D.Double(p,q); fiore@0: Rectangle2D bounds = new Rectangle2D.Double(); fiore@0: bounds.add(line.getBounds2D()); fiore@0: fiore@0: double width = bounds.getWidth(); fiore@0: double height = bounds.getHeight(); fiore@0: double scaleX = (BUTTON_SIZE - OFFSET)/ width; fiore@0: double scaleY = (BUTTON_SIZE - OFFSET)/ height; fiore@0: double scale = Math.min(scaleX, scaleY); fiore@0: fiore@0: AffineTransform oldTransform = g2.getTransform(); fiore@0: g2.translate(x, y); fiore@0: g2.scale(scale, scale); fiore@0: g2.translate(Math.max((height - width) / 2, 0), Math.max((width - height) / 2, 0)); fiore@0: fiore@0: g2.setColor(Color.black); fiore@0: g2.setStroke(e.getStyle().getStroke()); fiore@0: g2.draw(line); fiore@0: g2.setTransform(oldTransform); fiore@0: } fiore@0: }; fiore@0: final JButton button = new JButton(icon); fiore@5: button.setToolTipText(tip); fiore@0: button.setFocusable(false); fiore@0: fiore@0: button.addActionListener(new ActionListener(){ fiore@0: @Override fiore@0: public void actionPerformed(ActionEvent evt) { fiore@0: edgeCreatedListener.edgeCreated((Edge)e.clone()); fiore@0: }}); fiore@0: add(button); fiore@0: } fiore@0: fiore@5: /** fiore@5: * Sets the {@code EdgeCreatedListener} for this toolbar. Any previous set listener fiore@5: * will be overwritten. fiore@5: * fiore@5: * @param edgeCreatedListener the new {@code EdgeCreatedListener} for this toolbar fiore@5: */ fiore@5: public void setEdgeCreatedListener(EdgeCreatedListener edgeCreatedListener){ fiore@0: this.edgeCreatedListener = edgeCreatedListener; fiore@0: } fiore@0: fiore@0: private class NodeButton extends JToggleButton{ fiore@0: public NodeButton(Node node, Icon icon){ fiore@0: super(icon); fiore@0: setFocusable(false); fiore@0: this.node = node; fiore@0: } fiore@0: fiore@0: public Node getNode(){ fiore@0: return node; fiore@0: } fiore@0: Node node; fiore@0: } fiore@0: fiore@5: /** fiore@5: * The listener interface receiving events when the user clicks on fiore@5: * an {@code Edge} button. Unlike {@code Node} buttons which are {@code JTobbleButton} fiore@5: * objects to select the {@code Node} returned by {@code getSelectedTool()}, fiore@5: * the {@code Edge} buttons just trigger the registered listener with an immediate effect. fiore@5: */ fiore@0: public interface EdgeCreatedListener { fiore@5: /** fiore@5: * Invoked when an {@code Edge} button is pressed. fiore@5: * @param e the {@code Edge} related to the pressed button fiore@5: */ fiore@0: void edgeCreated(Edge e); fiore@0: } fiore@0: fiore@0: private ButtonGroup nodeButtonsGroup; fiore@0: private EdgeCreatedListener edgeCreatedListener; fiore@0: private NodeButton selectButton; fiore@0: fiore@0: private static final int BUTTON_SIZE = 30; fiore@0: private static final int OFFSET = 5; fiore@0: } fiore@0: