001 // This class is based on the MBSGUIFrame class, version 1 August 2002
002 // by Julie Zenekski
003
004 // Original copyright notice:
005
006 // AP(r) Computer Science Marine Biology Simulation:
007 // The PseudoInfiniteViewport class is copyright(c) 2002 College Entrance
008 // Examination Board (www.collegeboard.com).
009 //
010 // This class is free software; you can redistribute it and/or modify
011 // it under the terms of the GNU General Public License as published by
012 // the Free Software Foundation.
013 //
014 // This class is distributed in the hope that it will be useful,
015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017 // GNU General Public License for more details.
018
019 package view;
020
021 import controller.IDisplayAdapter;
022
023 import javax.swing.*;
024 import java.awt.*;
025 import java.awt.event.ActionEvent;
026 import java.awt.event.ActionListener;
027
028 /**
029 * The viewport for the display panel.
030 *
031 * @author Mathias Ricken
032 */
033
034 public class DisplayViewport extends JViewport {
035 /**
036 * The Pannable interface contains those methods the view installed in a PseudoInfiniteViewport needs to support to
037 * enable panning behavior along with scrolling.
038 */
039 public interface Pannable {
040 /**
041 * Pan by the specified amount.
042 *
043 * @param dx x-delta
044 * @param dy y delta
045 */
046 void pan(double dx, double dy);
047
048 /**
049 * Reset the pan.
050 */
051 void resetPan();
052
053 /**
054 * Get the size of the cells.
055 *
056 * @return size of cells
057 */
058 int getCellSize();
059
060 /**
061 * Get pan tooltip text.
062 *
063 * @return pan tip text.
064 */
065 String getPanTipText();
066 }
067
068 private static final int ORIGIN_TIP_DELAY = 1000;
069
070 /**
071 * Scroll pane controlled by this viewport.
072 */
073 private JScrollPane _scrollParent;
074
075 /**
076 * Panel for the tool tip.
077 */
078 private JPanel _glassPane;
079
080 /**
081 * Origin tool tip.
082 */
083 private JToolTip _originTip;
084
085 /**
086 * Tool tip timer.
087 */
088 private Timer _originTipTimer;
089
090 /**
091 * Display adapter.
092 */
093 private IDisplayAdapter _displayAdapter;
094
095 /**
096 * Center before last movement.
097 */
098 private Point.Double _lastOrigin = new Point.Double();
099
100 /**
101 * Make a new display viewport for the given scroll pane.
102 *
103 * @param parent the JScrollPane for which this will be the viewport
104 * @param da display adapter to connect to the model
105 */
106 public DisplayViewport(JScrollPane parent, IDisplayAdapter da) {
107 _scrollParent = parent;
108 _displayAdapter = da;
109 setBackground(Color.lightGray);
110 }
111
112 /**
113 * Reset the viewport.
114 */
115 public void resetViewport() {
116 Pannable p = getPannableView();
117 if (null != p) {
118 _lastOrigin = _displayAdapter.getViewPosition(new Point.Double(0, 0));
119 }
120 }
121
122 /**
123 * Set the old position of the view.
124 *
125 * @param pt old position of view
126 */
127 public void oldSetViewPosition(Point pt) {
128 super.setViewPosition(pt);
129 }
130
131 /**
132 * Sets the view position (upper left) to a new point. Overridden from JViewport.
133 *
134 * @param pt the Point to become the upper left
135 */
136 public void setViewPosition(Point pt) {
137 boolean isAdjusting = _scrollParent.getVerticalScrollBar().getValueIsAdjusting() || _scrollParent.getHorizontalScrollBar().getValueIsAdjusting();
138 Pannable p = getPannableView();
139 Point.Double delta;
140
141 boolean changed = !getViewPosition().equals(pt);
142 super.setViewPosition(pt);
143
144 if (null != p) {
145 if (!isAdjusting) {
146 // the user let go of the scrollbars
147 int cellSize = p.getCellSize();
148
149 // figure out how far the user scrolled
150 delta = new Point.Double((double)(pt.x) / cellSize - _lastOrigin.x,
151 (double)(pt.y) / cellSize - _lastOrigin.y);
152
153 // ask the environment how much to pan
154 delta = _displayAdapter.getPanDelta(delta);
155 // and pan
156 p.pan(delta.x * cellSize, delta.y * cellSize);
157
158 // convert position into model coordinate units
159 Point.Double modelPos = new Point.Double(((double)pt.x) / cellSize, ((double)pt.y) / cellSize);
160 // and ask the environment where to scroll
161 modelPos = _displayAdapter.getViewPosition(modelPos);
162 Point pixelPos = new Point((int)(modelPos.x * cellSize), (int)(modelPos.y * cellSize));
163 // then scroll there
164 super.setViewPosition(pixelPos);
165
166 _lastOrigin = new Point.Double((double)(pixelPos.x) / cellSize, (double)(pixelPos.y) / cellSize);
167
168 // update scrollbars and display
169 fireStateChanged();
170 repaint();
171 }
172 }
173
174 if (isAdjusting || changed) {
175 showOriginTip();
176 }
177 }
178
179 /**
180 * Return pannable view.
181 *
182 * @return pannable view
183 */
184 private Pannable getPannableView() {
185 return (Pannable)getView();
186 }
187
188 /**
189 * Show a tool tip over the upper left corner of the viewport with the contents of the pannable view's pannable tip
190 * text (typically a string identifiying the corner point). Tip is removed after a short delay.
191 */
192 public void showOriginTip() {
193 if (null == getRootPane()) {
194 return;
195 }
196 // drawFish in glass pane to appear on top of other components
197 if (null == _glassPane) {
198 getRootPane().setGlassPane(_glassPane = new JPanel());
199 _glassPane.setOpaque(false);
200 _glassPane.setLayout(null); // will control layout manually
201 _glassPane.add(_originTip = new JToolTip());
202 _originTipTimer = new Timer(ORIGIN_TIP_DELAY, new ActionListener() {
203 public void actionPerformed(ActionEvent evt) {
204 _glassPane.setVisible(false);
205 }
206 });
207 _originTipTimer.setRepeats(false);
208 }
209 String tipText = getPannableView().getPanTipText();
210 if (null == tipText) {
211 return;
212 }
213
214 // set tip text to identify current origin of pannable view
215 _originTip.setTipText(tipText);
216
217 // position tip to appear at upper left corner of viewport
218 _originTip.setLocation(SwingUtilities.convertPoint(this, getLocation(), _glassPane));
219 _originTip.setSize(_originTip.getPreferredSize());
220
221 // show glass pane (it contains tip)
222 _glassPane.setVisible(true);
223
224 // this timer will hide the glass pane after a short delay
225 _originTipTimer.restart();
226 }
227 }