001 package view;
002
003 import controller.IDisplayAdapter;
004 import controller.IEnvAdapter;
005
006 import javax.swing.*;
007 import java.awt.*;
008 import java.awt.event.MouseAdapter;
009 import java.awt.event.MouseEvent;
010 import java.awt.geom.AffineTransform;
011
012 /**
013 * Panel to display the environment.
014 *
015 * @author Mathias Ricken
016 */
017 public class DisplayPanel extends JPanel implements Scrollable, DisplayViewport.Pannable {
018 // Class constants
019 private static final int MIN_CELL_SIZE = 8;
020 private static final int DEFAULT_CELL_SIZE = 32;
021
022 /**
023 * Display adapter.
024 */
025 IDisplayAdapter _displayAdapter;
026
027 /**
028 * Cell size.
029 */
030 int _cellSize = DEFAULT_CELL_SIZE;
031
032 /**
033 * Origin column.
034 */
035 double _originX;
036
037 /**
038 * Origin row.
039 */
040 double _originY;
041
042 /**
043 * Mouse adapter for editing.
044 */
045 MouseAdapter _mouseAdapter;
046
047 /**
048 * State of tool tips.
049 */
050 private boolean _toolTipsEnabled;
051
052 /**
053 * Environment adapter.
054 */
055 IEnvAdapter _envAdapter;
056
057 /**
058 * Make a new display panel.
059 *
060 * @param da display adapter to use
061 * @param ea environment adapter to use
062 */
063 public DisplayPanel(IDisplayAdapter da, final IEnvAdapter ea) {
064 _displayAdapter = da;
065 _envAdapter = ea;
066 _mouseAdapter = new MouseAdapter() {
067 public void mousePressed(MouseEvent evt) {
068 double x = ((double)evt.getPoint().x) / _cellSize + _originX;
069 double y = ((double)evt.getPoint().y) / _cellSize + _originY;
070
071 ea.edit(new Point.Double(x, y), evt.getButton());
072 revalidate();
073 }
074 };
075 setToolTipsEnabled(true);
076 revalidate();
077 }
078
079 /**
080 * Enable or disable the mouse adapter for editing.
081 *
082 * @param enable true to enable
083 */
084 public void enableMouseAdapter(boolean enable) {
085 if (enable) {
086 addMouseListener(_mouseAdapter);
087 }
088 else {
089 removeMouseListener(_mouseAdapter);
090 }
091 }
092
093 /**
094 * Enable or disable showing of tooltip giving information about the environment object beneath the mouse.
095 *
096 * @param flag whether to enable/disable tool tips
097 */
098 public void setToolTipsEnabled(boolean flag) {
099 if (flag) {
100 ToolTipManager.sharedInstance().registerComponent(this);
101 }
102 else {
103 ToolTipManager.sharedInstance().unregisterComponent(this);
104 }
105 _toolTipsEnabled = flag;
106 }
107
108 /**
109 * Given a MouseEvent, determine what text to place in the floating tool tip when the the mouse hovers over this
110 * location. If the mouse is over a valid environment cell. we provide some information about the cell and its
111 * contents. This method is automatically called on mouse-moved events since we register for tool tips.
112 *
113 * @param evt the MouseEvent in question
114 *
115 * @return the tool tip string for this location
116 */
117 public String getToolTipText(MouseEvent evt) {
118 double x = ((double)evt.getPoint().x) / _cellSize + _originX;
119 double y = ((double)evt.getPoint().y) / _cellSize + _originY;
120 return _envAdapter.getToolTipText(new Point.Double(x, y));
121 }
122
123 /**
124 * Zoom in.
125 */
126 public void zoomIn() {
127 double oldOriginX = _originX;
128 double oldOriginY = _originY;
129 JViewport vp = (JViewport)getParent();
130 Point pt = vp.getViewPosition();
131 pt.x = (int)(((double)pt.x) / _cellSize + _originX);
132 pt.y = (int)(((double)pt.y) / _cellSize + _originY);
133
134 _cellSize *= 2;
135 setCorner(pt.x, pt.y);
136 _originX = oldOriginX;
137 _originY = oldOriginY;
138 revalidate();
139 }
140
141 /**
142 * Zoom out.
143 */
144 public void zoomOut() {
145 double oldOriginX = _originX;
146 double oldOriginY = _originY;
147 JViewport vp = (JViewport)getParent();
148 Point pt = vp.getViewPosition();
149 pt.x = (int)(((double)pt.x) / _cellSize + _originX);
150 pt.y = (int)(((double)pt.y) / _cellSize + _originY);
151
152 _cellSize = Math.max(MIN_CELL_SIZE, _cellSize / 2);
153 setCorner(pt.x, pt.y);
154 _originX = oldOriginX;
155 _originY = oldOriginY;
156 revalidate();
157 }
158
159 /**
160 * Make field (x,y) visible in top left corner.
161 *
162 * @param x x-coordinate
163 * @param y y-coordinate
164 */
165 public void setCorner(int x, int y) {
166 DisplayViewport vp = (DisplayViewport)getParent();
167 vp.oldSetViewPosition(new Point((int)(x - _originX) * _cellSize, (int)(y - _originY) * _cellSize));
168 }
169
170 /**
171 * Returns the desired size of the display, for use by layout manager.
172 *
173 * @return preferred size
174 */
175 public Dimension getPreferredSize() {
176 Dimension s = _displayAdapter.getDisplaySize();
177 return new Dimension(s.width * _cellSize, s.height * _cellSize);
178 }
179
180 /**
181 * Returns the minimum size of the display, for use by layout manager.
182 *
183 * @return minimum size
184 */
185 public Dimension getMinimumSize() {
186 Dimension s = _displayAdapter.getDisplaySize();
187 return new Dimension(s.width * MIN_CELL_SIZE, s.height * MIN_CELL_SIZE);
188 }
189
190 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
191 return _cellSize;
192 }
193
194 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
195 if (SwingConstants.VERTICAL == orientation) {
196 return (int)(visibleRect.height * .9);
197 }
198 else {
199 return (int)(visibleRect.width * .9);
200 }
201 }
202
203 public boolean getScrollableTracksViewportWidth() {
204 return false;
205 }
206
207 public boolean getScrollableTracksViewportHeight() {
208 return false;
209 }
210
211 public Dimension getPreferredScrollableViewportSize() {
212 return new Dimension(420, 420);
213 }
214
215 /**
216 * Paint this component.
217 *
218 * @param g the Graphics object to use to render this component
219 */
220 public void paintComponent(Graphics g) {
221 Graphics2D g2 = (Graphics2D)g;
222 super.paintComponent(g2);
223
224 Rectangle curClip = g2.getClipBounds();
225 Graphics2D envGraphics = (Graphics2D)g2.create();
226 AffineTransform oldTransform = envGraphics.getTransform();
227 int left = getInsets().left;
228 int top = getInsets().top;
229
230 // transform to model coordinates
231 int x1 = (curClip.x - left) / _cellSize;
232 int y1 = (curClip.y - top) / _cellSize;
233 int x2 = (curClip.x + curClip.width - left + _cellSize - 1) / _cellSize;
234 int y2 = (curClip.y + curClip.height - top + _cellSize - 1) / _cellSize;
235
236 // translate and scale so that (0,0) corresponds to the field (x1,y1) in model coordinates
237 // and each field in model coordinates is MODEL_CELL_SIZE wide
238 envGraphics.translate(x1 * _cellSize + left, y1 * _cellSize + top);
239 envGraphics.scale(_cellSize, _cellSize);
240 envGraphics.setStroke(new BasicStroke(1.0f / _cellSize));
241 _displayAdapter.draw(envGraphics,
242 this,
243 new Point.Double(x1 + (int)_originX, y1 + (int)_originY),
244 new Point.Double(x2 + (int)_originX, y2 + (int)_originY));
245
246 envGraphics.setTransform(oldTransform);
247 }
248
249 /**
250 * Pan the panel by the specified amount of pixels.
251 *
252 * @param dx horizontal pan in pixels
253 * @param dy vertical pan in pixels
254 */
255 public void pan(double dx, double dy) {
256 _originX = (int)(_originX + dx / _cellSize);
257 _originY = (int)(_originY + dy / _cellSize);
258 }
259
260 /**
261 * Return the current cell size.
262 *
263 * @return cell size
264 */
265 public int getCellSize() {
266 return _cellSize;
267 }
268
269 /**
270 * Reset the pan.
271 */
272 public void resetPan() {
273 Point.Double center = _displayAdapter.getViewPosition(new Point.Double(0, 0));
274 _originX = -center.x;
275 _originY = -center.y;
276 repaint();
277 }
278
279 /**
280 * Get the tool tip text for panning.
281 *
282 * @return pan tool tip
283 */
284 public String getPanTipText() {
285 JViewport vp = (JViewport)getParent();
286 Point pt = vp.getViewPosition();
287 int x = (int)(((double)pt.x) / _cellSize + _originX);
288 int y = (int)(((double)pt.y) / _cellSize + _originY);
289 return "(" + x + ',' + y + ')';
290 }
291 }