fiore@0
|
1 /*
|
fiore@0
|
2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
|
fiore@0
|
3
|
fiore@0
|
4 Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com)
|
fiore@0
|
5 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
|
fiore@0
|
6
|
fiore@0
|
7 This program is free software: you can redistribute it and/or modify
|
fiore@0
|
8 it under the terms of the GNU General Public License as published by
|
fiore@0
|
9 the Free Software Foundation, either version 3 of the License, or
|
fiore@0
|
10 (at your option) any later version.
|
fiore@0
|
11
|
fiore@0
|
12 This program is distributed in the hope that it will be useful,
|
fiore@0
|
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
fiore@0
|
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
fiore@0
|
15 GNU General Public License for more details.
|
fiore@0
|
16
|
fiore@0
|
17 You should have received a copy of the GNU General Public License
|
fiore@0
|
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
fiore@0
|
19 */
|
fiore@0
|
20
|
fiore@0
|
21 package uk.ac.qmul.eecs.ccmi.gui;
|
fiore@0
|
22
|
fiore@0
|
23 import java.awt.Color;
|
fiore@0
|
24 import java.awt.Component;
|
fiore@0
|
25 import java.awt.Graphics;
|
fiore@0
|
26 import java.awt.Graphics2D;
|
fiore@0
|
27 import java.awt.event.ActionEvent;
|
fiore@0
|
28 import java.awt.event.ActionListener;
|
fiore@0
|
29 import java.awt.geom.AffineTransform;
|
fiore@0
|
30 import java.awt.geom.Line2D;
|
fiore@0
|
31 import java.awt.geom.Point2D;
|
fiore@0
|
32 import java.awt.geom.Rectangle2D;
|
fiore@0
|
33 import java.util.Enumeration;
|
fiore@0
|
34 import java.util.ResourceBundle;
|
fiore@0
|
35
|
fiore@0
|
36 import javax.swing.ButtonGroup;
|
fiore@0
|
37 import javax.swing.Icon;
|
fiore@0
|
38 import javax.swing.JButton;
|
fiore@0
|
39 import javax.swing.JToggleButton;
|
fiore@0
|
40 import javax.swing.JToolBar;
|
fiore@0
|
41
|
fiore@0
|
42
|
fiore@0
|
43 /**
|
fiore@0
|
44 A Toolbar that contains node and edge prototype icons. By using the toolbar
|
fiore@0
|
45 the user can create nodes and edges in the diagram out of clonation from the prototype.
|
fiore@0
|
46 */
|
fiore@0
|
47 @SuppressWarnings("serial")
|
fiore@0
|
48 public class GraphToolbar extends JToolBar {
|
fiore@5
|
49 /**
|
fiore@5
|
50 * Constructs a tool bar with no icons.
|
fiore@5
|
51 *
|
fiore@5
|
52 * @param diagram the diagram this toolbar is related to
|
fiore@0
|
53 */
|
fiore@0
|
54 public GraphToolbar(Diagram diagram){
|
fiore@0
|
55 /* creates icon for select button */
|
fiore@0
|
56 Icon icon = new Icon(){
|
fiore@0
|
57 public int getIconHeight() { return BUTTON_SIZE; }
|
fiore@0
|
58 public int getIconWidth() { return BUTTON_SIZE; }
|
fiore@0
|
59 public void paintIcon(Component c, Graphics g,
|
fiore@0
|
60 int x, int y){
|
fiore@0
|
61 Graphics2D g2 = (Graphics2D)g;
|
fiore@0
|
62 GraphPanel.drawGrabber(g2, x + OFFSET, y + OFFSET);
|
fiore@0
|
63 GraphPanel.drawGrabber(g2, x + OFFSET, y + BUTTON_SIZE - OFFSET);
|
fiore@0
|
64 GraphPanel.drawGrabber(g2, x + BUTTON_SIZE - OFFSET, y + OFFSET);
|
fiore@0
|
65 GraphPanel.drawGrabber(g2, x + BUTTON_SIZE - OFFSET, y + BUTTON_SIZE - OFFSET);
|
fiore@0
|
66 }
|
fiore@0
|
67 };
|
fiore@0
|
68 /* add selection button */
|
fiore@0
|
69 ResourceBundle resources =
|
fiore@0
|
70 ResourceBundle.getBundle(EditorFrame.class.getName());
|
fiore@0
|
71 String text = resources.getString("grabber.text");
|
fiore@0
|
72 selectButton = new NodeButton(null,icon);
|
fiore@0
|
73 selectButton.setToolTipText(text);
|
fiore@0
|
74 nodeButtonsGroup = new ButtonGroup();
|
fiore@0
|
75 nodeButtonsGroup.add(selectButton);
|
fiore@0
|
76 add(selectButton);
|
fiore@0
|
77
|
fiore@0
|
78 /* add diagram buttons to the toolbar */
|
fiore@0
|
79 Node[] nodeTypes = diagram.getNodePrototypes();
|
fiore@0
|
80 for (int i = 0; i < nodeTypes.length; i++){
|
fiore@0
|
81 text = nodeTypes[i].getType();
|
fiore@0
|
82 add(nodeTypes[i], text );
|
fiore@0
|
83 }
|
fiore@0
|
84
|
fiore@0
|
85 /* select the select-button as default */
|
fiore@0
|
86 nodeButtonsGroup.setSelected(selectButton.getModel(), true);
|
fiore@0
|
87
|
fiore@0
|
88 /* separate node buttons from edge buttons */
|
fiore@0
|
89 addSeparator();
|
fiore@0
|
90
|
fiore@0
|
91 /* add diagram edges to the toolbar */
|
fiore@0
|
92 Edge[] edgeTypes = diagram.getEdgePrototypes();
|
fiore@0
|
93 for (int i = 0; i < edgeTypes.length; i++){
|
fiore@0
|
94 text = edgeTypes[i].getType();
|
fiore@0
|
95 add(edgeTypes[i], text );
|
fiore@0
|
96 }
|
fiore@0
|
97 }
|
fiore@0
|
98
|
fiore@0
|
99 /**
|
fiore@0
|
100 Gets the node prototype that is associated with
|
fiore@0
|
101 the currently selected button
|
fiore@5
|
102 @return a {@code Node} prototype
|
fiore@0
|
103 */
|
fiore@0
|
104 public Node getSelectedTool() {
|
fiore@0
|
105 @SuppressWarnings("rawtypes")
|
fiore@0
|
106 Enumeration elements = nodeButtonsGroup.getElements();
|
fiore@0
|
107 while (elements.hasMoreElements()) {
|
fiore@0
|
108 NodeButton b = (NodeButton)elements.nextElement();
|
fiore@0
|
109 if (b.isSelected()) {
|
fiore@0
|
110 /* switch back to the select-button */
|
fiore@0
|
111 nodeButtonsGroup.setSelected(selectButton.getModel(), true);
|
fiore@0
|
112 return b.getNode();
|
fiore@0
|
113 }
|
fiore@0
|
114 }
|
fiore@0
|
115 /* getting here means the selection button is selected */
|
fiore@0
|
116 return null;
|
fiore@0
|
117 }
|
fiore@0
|
118
|
fiore@0
|
119 /**
|
fiore@0
|
120 Adds a node to the tool bar.
|
fiore@0
|
121 @param n the node to add
|
fiore@5
|
122 @param tip the tool tip appearing when hovering on this edge button
|
fiore@0
|
123 */
|
fiore@5
|
124 public void add(final Node n, String tip){
|
fiore@0
|
125 Icon icon = new Icon(){
|
fiore@0
|
126 public int getIconHeight() { return BUTTON_SIZE; }
|
fiore@0
|
127 public int getIconWidth() { return BUTTON_SIZE; }
|
fiore@0
|
128 public void paintIcon(Component c, Graphics g,
|
fiore@0
|
129 int x, int y){
|
fiore@0
|
130 double width = n.getBounds().getWidth();
|
fiore@0
|
131 double height = n.getBounds().getHeight();
|
fiore@0
|
132 Graphics2D g2 = (Graphics2D)g;
|
fiore@0
|
133 double scaleX = (BUTTON_SIZE - OFFSET)/ width;
|
fiore@0
|
134 double scaleY = (BUTTON_SIZE - OFFSET)/ height;
|
fiore@0
|
135 double scale = Math.min(scaleX, scaleY);
|
fiore@0
|
136
|
fiore@0
|
137 AffineTransform oldTransform = g2.getTransform();
|
fiore@0
|
138 g2.translate(x, y);
|
fiore@0
|
139 g2.translate(OFFSET/2*scaleX,OFFSET/2*scaleY);
|
fiore@0
|
140 g2.scale(scale, scale);
|
fiore@0
|
141 g2.setColor(Color.black);
|
fiore@0
|
142 n.draw(g2);
|
fiore@0
|
143 g2.setTransform(oldTransform);
|
fiore@0
|
144 }
|
fiore@0
|
145 };
|
fiore@0
|
146
|
fiore@0
|
147 NodeButton button = new NodeButton(n, icon);
|
fiore@5
|
148 button.setToolTipText(tip);
|
fiore@0
|
149
|
fiore@0
|
150 add(button);
|
fiore@0
|
151 nodeButtonsGroup.add(button);
|
fiore@0
|
152 }
|
fiore@0
|
153
|
fiore@0
|
154 /**
|
fiore@0
|
155 Adds an edge to the tool bar.
|
fiore@5
|
156 @param e the edge to add
|
fiore@5
|
157 @param tip the tool tip appearing when hovering on this edge button
|
fiore@0
|
158 */
|
fiore@5
|
159 public void add(final Edge e, String tip){
|
fiore@0
|
160 Icon icon = new Icon(){
|
fiore@0
|
161 public int getIconHeight() { return BUTTON_SIZE; }
|
fiore@0
|
162 public int getIconWidth() { return BUTTON_SIZE; }
|
fiore@0
|
163 public void paintIcon(Component c, Graphics g,
|
fiore@0
|
164 int x, int y){
|
fiore@0
|
165 Graphics2D g2 = (Graphics2D)g;
|
fiore@0
|
166 /* create two points */
|
fiore@0
|
167 Point2D p = new Point2D.Double();
|
fiore@0
|
168 Point2D q = new Point2D.Double();
|
fiore@0
|
169 p.setLocation(OFFSET, OFFSET);
|
fiore@0
|
170 q.setLocation(BUTTON_SIZE - OFFSET, BUTTON_SIZE - OFFSET);
|
fiore@0
|
171
|
fiore@0
|
172 Line2D line = new Line2D.Double(p,q);
|
fiore@0
|
173 Rectangle2D bounds = new Rectangle2D.Double();
|
fiore@0
|
174 bounds.add(line.getBounds2D());
|
fiore@0
|
175
|
fiore@0
|
176 double width = bounds.getWidth();
|
fiore@0
|
177 double height = bounds.getHeight();
|
fiore@0
|
178 double scaleX = (BUTTON_SIZE - OFFSET)/ width;
|
fiore@0
|
179 double scaleY = (BUTTON_SIZE - OFFSET)/ height;
|
fiore@0
|
180 double scale = Math.min(scaleX, scaleY);
|
fiore@0
|
181
|
fiore@0
|
182 AffineTransform oldTransform = g2.getTransform();
|
fiore@0
|
183 g2.translate(x, y);
|
fiore@0
|
184 g2.scale(scale, scale);
|
fiore@0
|
185 g2.translate(Math.max((height - width) / 2, 0), Math.max((width - height) / 2, 0));
|
fiore@0
|
186
|
fiore@0
|
187 g2.setColor(Color.black);
|
fiore@0
|
188 g2.setStroke(e.getStyle().getStroke());
|
fiore@0
|
189 g2.draw(line);
|
fiore@0
|
190 g2.setTransform(oldTransform);
|
fiore@0
|
191 }
|
fiore@0
|
192 };
|
fiore@0
|
193 final JButton button = new JButton(icon);
|
fiore@5
|
194 button.setToolTipText(tip);
|
fiore@0
|
195 button.setFocusable(false);
|
fiore@0
|
196
|
fiore@0
|
197 button.addActionListener(new ActionListener(){
|
fiore@0
|
198 @Override
|
fiore@0
|
199 public void actionPerformed(ActionEvent evt) {
|
fiore@0
|
200 edgeCreatedListener.edgeCreated((Edge)e.clone());
|
fiore@0
|
201 }});
|
fiore@0
|
202 add(button);
|
fiore@0
|
203 }
|
fiore@0
|
204
|
fiore@5
|
205 /**
|
fiore@5
|
206 * Sets the {@code EdgeCreatedListener} for this toolbar. Any previous set listener
|
fiore@5
|
207 * will be overwritten.
|
fiore@5
|
208 *
|
fiore@5
|
209 * @param edgeCreatedListener the new {@code EdgeCreatedListener} for this toolbar
|
fiore@5
|
210 */
|
fiore@5
|
211 public void setEdgeCreatedListener(EdgeCreatedListener edgeCreatedListener){
|
fiore@0
|
212 this.edgeCreatedListener = edgeCreatedListener;
|
fiore@0
|
213 }
|
fiore@0
|
214
|
fiore@0
|
215 private class NodeButton extends JToggleButton{
|
fiore@0
|
216 public NodeButton(Node node, Icon icon){
|
fiore@0
|
217 super(icon);
|
fiore@0
|
218 setFocusable(false);
|
fiore@0
|
219 this.node = node;
|
fiore@0
|
220 }
|
fiore@0
|
221
|
fiore@0
|
222 public Node getNode(){
|
fiore@0
|
223 return node;
|
fiore@0
|
224 }
|
fiore@0
|
225 Node node;
|
fiore@0
|
226 }
|
fiore@0
|
227
|
fiore@5
|
228 /**
|
fiore@5
|
229 * The listener interface receiving events when the user clicks on
|
fiore@5
|
230 * an {@code Edge} button. Unlike {@code Node} buttons which are {@code JTobbleButton}
|
fiore@5
|
231 * objects to select the {@code Node} returned by {@code getSelectedTool()},
|
fiore@5
|
232 * the {@code Edge} buttons just trigger the registered listener with an immediate effect.
|
fiore@5
|
233 */
|
fiore@0
|
234 public interface EdgeCreatedListener {
|
fiore@5
|
235 /**
|
fiore@5
|
236 * Invoked when an {@code Edge} button is pressed.
|
fiore@5
|
237 * @param e the {@code Edge} related to the pressed button
|
fiore@5
|
238 */
|
fiore@0
|
239 void edgeCreated(Edge e);
|
fiore@0
|
240 }
|
fiore@0
|
241
|
fiore@0
|
242 private ButtonGroup nodeButtonsGroup;
|
fiore@0
|
243 private EdgeCreatedListener edgeCreatedListener;
|
fiore@0
|
244 private NodeButton selectButton;
|
fiore@0
|
245
|
fiore@0
|
246 private static final int BUTTON_SIZE = 30;
|
fiore@0
|
247 private static final int OFFSET = 5;
|
fiore@0
|
248 }
|
fiore@0
|
249
|