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    }