📄 zoomablewfpanel.java
字号:
package org.trinet.jiggle;
import java.util.Observable;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Dimension;
import javax.swing.JViewport;
import javax.swing.JScrollPane;
//import java.applet.*;
import java.text.*;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import org.trinet.jasi.*;
import org.trinet.util.TimeSpan;
/**
* ZoomableWFPanel.java
*
* Created: Tue Nov 2 14:12:35 1999
*
* @author Doug Given
* @version
*/
/**
* An active panel that can change scale dynamically. It must be contained in a
* JScrollPane which manages a JViewport through which part of this component is
* visible at any given time. "Zooming" this WFPanel involves resizing it so
* that the part of it that can be seen in the JViewport is at the desired
* scale. It rescales on every repaint to accomodate resizes of the containing
* component.<br>
*
* Scaling and centering are done relative to the MasterWFVModel WFSelectionBox.
*
* @See: javax.swing.JScrollPane */
public class ZoomableWFPanel extends ActiveWFPanel {
// ActiveWFPanel extends WFPanel & implements Scrollable
// Could over-ride the Scrollable methods here to customize behavior.
/** The JViewport that represents the "window" in the containing component
* (usu. a JScrollPane) that maps to the visible portion of this panel.
* This panel is scaled so that the its WFSelectionBox fills the JViewport.
*/
//JViewport vport;
BombSightViewport vport;
/** JScrollPane that manages a JViewport that represents the visible
* part of this WFPanel. */
JScrollPane scrollPane;
// debugging
int oldX = 0;
int oldY = 0;
// listen for changes to selected channel or time/amp window
// Overrides ActiveWFPanel's listeners.
WFWindowListener wfwChangeListener = new WFWindowListener();
WFViewListener wfvChangeListener = new WFViewListener();
/**
* Constructor requires a JScrollPane so that the WFSelectionBox of the
* ZoomableWFPanel can be scaled
* to fit in the JViewport of the JScrollPane.
*/
public ZoomableWFPanel (JScrollPane scrollPane, MasterView mv) {
// super() adds the superclasse's mouse listeners
super();
setMasterView(mv);
// this is different from ActiveWFPanel which only adds listeners when it's
// selected. We are ALWAYS active.
addListeners (mv);
setJScrollPane (scrollPane);
// some behavior flags specific to Zoomable panels
showTimeScale = false;
showChannelLabel = false;
}
/** Sets the JScrollPane that will control this panel. And adds this
* panel to the JScrollPane's JViewport. */
public void setJScrollPane (JScrollPane scrollPane) {
this.scrollPane = scrollPane;
if (scrollPane != null) {
vport = (BombSightViewport) scrollPane.getViewport();
vport.add(this); // add ourself to the viewport
}
}
/**
* Tell Layout managers etal what size we want to be. This panel's size is based on
* scaling the MasterWFVModel's WFSelectionBox to the current JViewport's size.
*/
// This solved several problems that resulted in missing panels because they
// dimensions of (0, 0)
public Dimension getPreferredSize () {
WFSelectionBox selBox =
wfv.mv.masterWFWindowModel.getSelectionBox(getWf());
// This is a simple proportion calc. Wp/Wv = Tp/Tv :. Wp = Wv*(Tp/Tv)
int newWidth = (int) ( vport.getWidth() *
(panelBox.getTimeSize() / selBox.getTimeSize()) );
int newHeight = (int) ( vport.getHeight() *
(panelBox.getAmpSize() /selBox.getAmpSize()) );
// make sure we don't get too small
newWidth = (int) Math.max (newWidth, getMinimumSize().getWidth());
newHeight = (int) Math.max (newHeight, getMinimumSize().getHeight());
return new Dimension (newWidth, newHeight);
}
/** Returns the minimum size for this component in pixels. It can't shink smaller then
* the the viewport, :. the min height = the viewport height. However, the
* width must be TWICE the viewport width to allow the first and last point
* in the seismogram to be centered on the bombsite. <p>
*
<tt>
*<-----JViewport------>
*+--------------------+********************
*| | | . *
*| |<----seismogram----->. *
*| |/\/\/\/\/\/\/\/\/\/\/. *
*| | | . *
*| | | . *
*+--------------------+********************
*<-------------ZoomableWFPanel------------>
</tt> */
public Dimension getMinimumSize () {
return new Dimension (vport.getWidth() * 2, vport.getHeight());
}
/**
* Set the size of the panel box size have "insets" or white space around the
* edge so you can center the first and last samples.
*/
public void setPanelBoxSize () {
if (wfv == null) return;
TimeSpan ts = wfv.viewSpan;
double pad = ts.getDuration()/2.0;
TimeSpan tsNew = new TimeSpan(ts.getStart() - pad, ts.getEnd() + pad);
Waveform wf = getWf();
if (wf != null) {
panelBox.set(tsNew, wf.getMaxAmp(), wf.getMinAmp());
} else {
// box must have non-zero dimension else /0 error happen elsewhere
panelBox.set(tsNew, 1, 0);
}
}
/** Override so the background is NOT painted in the high color. */
public void paintBackground (Graphics g) {
setSelected(false);
super.paintBackground(g);
}
/** Return a WFSelectionBox representing the part of this WFPanel that is
* visible in the ViewPort. */
public WFSelectionBox getVisibleBox() {
Rectangle vrect = vport.getViewRect();
double maxAmp = ampOfPixel((int)Math.round(vrect.getMinY()));
double minAmp = ampOfPixel((int)Math.round(vrect.getMaxY()));
double startTime = dtOfPixel((int)Math.round(vrect.getMinX()));
double endTime = dtOfPixel((int)Math.round(vrect.getMaxX()));
return new WFSelectionBox(startTime, endTime,
maxAmp, minAmp);
}
/**
* Change the size of the MasterWFVModel WFSelectionBox by a scaling factor
* which forces the panel to rescale A magnification of "2" plots twice as big
* while 0.5 plots half as big. This really just changes the panel's size and
* recalculates scaling factors. */
public void setScale (double magX, double magY)
{
// resize the selectionBox, it expands equally in both directions so that the
// center in time and amp are the same. The panel will be automatically resized
// Note that when you EXPAND the scale by 2x you are actually SHRINKING the
// selection area by 1/2, :. 1.0/magX
WFSelectionBox selBox =
wfv.mv.masterWFWindowModel.getSelectionBox(getWf());
// do x (time) scale
if (magX != 1.0 ) {
double oldCenterTime = selBox.getCenterTime();
// min disallows making view too small
double newTimeSpan = Math.min(dataBox.getTimeSize(),
selBox.getTimeSize()/magX);
double halfTime = newTimeSpan/2.0;
selBox.setTimeSpan(oldCenterTime-halfTime, oldCenterTime+halfTime);
}
// change y (amp) scale
if (magY != 1.0) {
double oldCenterAmp = selBox.getCenterAmp();
double newAmpSize = Math.min(dataBox.getAmpSize(),
selBox.getAmpSize()/magY);
double halfAmp = newAmpSize/2.0;
selBox.setAmpSpan(oldCenterAmp+halfAmp, oldCenterAmp-halfAmp);
}
mv.masterWFWindowModel.set(getWf(), selBox); // notify all listeners of the change
}
/**
* Set the WFPanel's size and scaling factors so that the WFSelectionBox fits in
* the JViewport. This overides WFPanel.scalePanel() which is called on every
* WFPanel.repaint to accomodate window resizes, etc. This may change the
* aspect ratio. */
//TODO: might be better to do
// scrollRectToVisible( 0, 0, getSize().width, getSize().height ) ??
protected void scalePanel () {
WFSelectionBox selBox =
wfv.mv.masterWFWindowModel.getSelectionBox(getWf());
// adjust WFPanel size only if it has changed
// ATTEMPT TO STOP CREEP
if (!getSize().equals(getPreferredSize())) setSize (getPreferredSize());
// Set scaling factors used for painting
setScaleFactors (panelBox);
// Center
// don't want to fire scroll events because that causes an infinite event loop
removeListeners();
centerViewportOnBox(selBox, false);
addListeners();
}
/**
* Center viewport on the given WFSelectionBox.
*/
void centerViewportOnBox (WFSelectionBox box, boolean fireEvents) {
// get origin; upper left corner of the box
int x0 = pixelOfTime(box.getStartTime());
int y0 = pixelOfAmp (box.getMaxAmp());
/*
This is a KLUDGE that prevents the "crawl" behavior.
This method calls vport.setViewPosition(newPos) which fires a scroll event
which calls the ZoomPanel.ScrollListener(). That adjusts the WFViewModel which
notifies all the listeners. This panel is also a listener and
its listener method calls repaint() which calls paintComponent() which calls
scalePanel() which calls centerViewport() AGAIN. And so on, recursively.
The test below is intended to stop the recursion if no real change has
occured HOWEVER somehow the position is actually getting changed!
I suspect it is in "precision jitter" when the ZoomPanel.ScrollListener()
recalculates the WFSelectionBox from the viewport dimensions and the scale factors.
The viewport x position always seems to get incremented by 1, thus the
Math.abs(oldX-x0) <= 1 test is a test for -no change-.
*/
if ( Math.abs(oldX-x0) <= 1 &&
oldY == y0) return; // crawls, amp moves
oldX = x0;
oldY = y0;
Point newPos = new Point(x0, y0);
// inhibit change events to stop hopping about
/// boolean oldState = vport.changeEventsEnabled();
/// vport.enableChangeEvents(fireEvents);
vport.setViewPosition (newPos); // this fires a scroll event
/// vport.enableChangeEvents(oldState);
}
// Had to override ActiveWFPanel.removeListeners and .addListeners
// a) The Zoomable does NOT react to scroll events
// b) It you don't, type super() loads its listeners not ours
/** Remove mouse listeners and MVC model */
protected void removeListeners() {
if (mv != null) {
if (mv.masterWFViewModel != null)
mv.masterWFViewModel.removeChangeListener(wfvChangeListener);
if (mv.masterWFWindowModel != null)
mv.masterWFWindowModel.removeChangeListener(wfwChangeListener);
// phase, amp and coda listeners
removeReadingListeners();
}
}
/** Add model listeners */
protected void addListeners() {
addListeners (mv);
}
/** Add model listeners to this MasterView's models. */
protected void addListeners (MasterView mv) {
if (mv != null) {
// listener for selected panel changes
if (mv.masterWFViewModel != null) {
mv.masterWFViewModel.addChangeListener(wfvChangeListener);
}
// use same listener for selected viewport changes
if (mv.masterWFWindowModel != null) {;
mv.masterWFWindowModel.addChangeListener(wfwChangeListener);
}
// phase, amp and coda listeners
addReadingListeners();
}
}
/** INNER CLASS: Handle changes to the selected channel. */
class WFViewListener implements ChangeListener {
public void stateChanged (ChangeEvent changeEvent) {
WFView newWFV = ((SelectedWFViewModel) changeEvent.getSource()).get();
if (newWFV != wfv) {
setWFView(newWFV); // replace the WFView
// setWFView does a repaint
// repaint();
}
}
}
/** INNER CLASS: Handle changes to the selected time/amp window. */
class WFWindowListener implements ChangeListener {
public void stateChanged (ChangeEvent changeEvent) {
// System.out.println ("Zoom WFWindowListener... ");
repaint();
}
} // end of WFWindowListener
/** **************************************************************************************
* Scrollable interface methods
* These methods determine the behavior of this component if it is placed in a JScrollPane.
* @See: Scrollable
*/
boolean trackViewportX = false;
boolean trackViewportY = false;
public Dimension getPreferredScrollableViewportSize()
{
return getPreferredSize();
}
public boolean getScrollableTracksViewportWidth()
{
return trackViewportX;
}
public boolean getScrollableTracksViewportHeight()
{
return trackViewportY;
}
public void setScrollableTracksViewportWidth(boolean tf)
{
trackViewportX = tf;;
}
public void setScrollableTracksViewportHeight(boolean tf)
{
trackViewportY = tf;
}
/**
* Determines scrolling increment when "track" is clicked. (Coarse scrolling)
* For WFPanel the default is 1/10 of the WFPanel dimension. Here we override
* this method to change it to 50% of the viewport size.
*/
public int getScrollableBlockIncrement (Rectangle visRec, int orient, int dir)
{
if (orient == SwingConstants.VERTICAL)
{
int move = (int) Math.rint(vport.getHeight() * 0.5);
System.out.println ("Vblock move = "+move);
return move; // 90% of height
} else {
int move = (int) Math.rint(vport.getWidth() * 0.5);
System.out.println ("Hblock move = "+move);
return move; // 90% of width
}
}
/**
* Determines scrolling increment when arrow widgit is clicked. (Fine scrolling)
* Default in vertical is 1 sample or 1 pixel, whichever is larger. Default in horizontal
* is 1/100 the height or 1 pixel
*/
//TODO: because of rounding, repeated scroll jumps cause cumulative error in whole.
// :. should calc. jump amt. to be explicit to next sample.
public int getScrollableUnitIncrement (Rectangle visRec, int orient, int dir)
{
if (orient == SwingConstants.VERTICAL)
{
// return Math.max(1,(int) pixelsPerInterval);
int jump = Math.max(1, this.getHeight() / 100);
// System.out.println ("Vunit move = "+jump);
return jump;
} else {
int jump = (int) Math.rint(getWf().getSampleInterval() * pixelsPerSecond);
if (jump < 1) jump = 1;
// System.out.println ("Hunit move = "+jump);
return jump ;
}
}
/*
* End of Scrollable interface methods
************************************************************* */
} // ZoomableWFPanel
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -