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 }