mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 00:57:31 -05:00
Updates pull request from Roman Tsypuk to convert the build system from
ant to gradle. Removes linux and windows application launch scripts and produces an executable jar with all resources included.
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
package io.github.dsheirer.gui.control;
|
||||
|
||||
import io.github.dsheirer.buffer.CircularBuffer;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.util.List;
|
||||
|
||||
public class ConstellationViewer extends JPanel implements Listener<Complex>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int mSampleRate;
|
||||
private int mSymbolRate;
|
||||
private float mSamplesPerSymbol;
|
||||
private float mCounter = 0;
|
||||
private float mOffset = 0;
|
||||
private CircularBuffer<Complex> mBuffer =
|
||||
new CircularBuffer<Complex>( 5000 );
|
||||
private Complex mPrevious = new Complex( 1, 1 );
|
||||
|
||||
public ConstellationViewer( int sampleRate, int symbolRate )
|
||||
{
|
||||
mSampleRate = sampleRate;
|
||||
mSymbolRate = symbolRate;
|
||||
mSamplesPerSymbol = (float)mSampleRate / (float)mSymbolRate;
|
||||
|
||||
initGui();
|
||||
}
|
||||
|
||||
private void initGui()
|
||||
{
|
||||
setPreferredSize( new Dimension( 200,200 ) );
|
||||
|
||||
addMouseListener( new MouseListener()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
if( SwingUtilities.isRightMouseButton( e ) )
|
||||
{
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
menu.add( new TimingOffsetItem( (int)( mSamplesPerSymbol * 10 ), (int)( mOffset * 10 ) ) );
|
||||
|
||||
menu.show( ConstellationViewer.this, e.getX(), e.getY() );
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseReleased( MouseEvent e ) {}
|
||||
public void mousePressed( MouseEvent e ) {}
|
||||
public void mouseExited( MouseEvent e ) {}
|
||||
public void mouseEntered( MouseEvent e ) {}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Complex sample )
|
||||
{
|
||||
mBuffer.receive( sample );
|
||||
|
||||
Complex angle = Complex.multiply( sample, mPrevious.conjugate() );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timing offset ( 0 <> SamplesPerSymbol ) for selecting which
|
||||
* sample to plot, within the symbol timeframe. Values greater than the
|
||||
* samples per symbol value will simply wrap or delay into the next symbol
|
||||
* period.
|
||||
*
|
||||
* @param offset
|
||||
*/
|
||||
public void setOffset( float offset )
|
||||
{
|
||||
mOffset = offset;
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent( Graphics g )
|
||||
{
|
||||
super.paintComponent( g );
|
||||
|
||||
Graphics2D graphics = (Graphics2D) g;
|
||||
|
||||
graphics.setColor( Color.BLUE );
|
||||
|
||||
List<Complex> samples = mBuffer.getElements();
|
||||
|
||||
double centerX = (double)getHeight() / 2.0d;
|
||||
double centerY = (double)getWidth() / 2.0d;
|
||||
|
||||
double scale = 0.5d;
|
||||
|
||||
mCounter = 0;
|
||||
|
||||
for( Complex sample: samples )
|
||||
{
|
||||
if( mCounter > ( mOffset + mSamplesPerSymbol ) )
|
||||
{
|
||||
/**
|
||||
* Multiply the current sample against the complex conjugate of the
|
||||
* previous sample to derive the phase delta between the two samples
|
||||
*
|
||||
* Negating the previous sample quadrature produces the conjugate
|
||||
*/
|
||||
double i = ( sample.inphase() * mPrevious.inphase() ) -
|
||||
( sample.quadrature() * -mPrevious.quadrature() );
|
||||
double q = ( sample.quadrature() * mPrevious.inphase() ) +
|
||||
( sample.inphase() * -mPrevious.quadrature() );
|
||||
|
||||
double angle;
|
||||
|
||||
//Check for divide by zero
|
||||
if( i == 0 )
|
||||
{
|
||||
angle = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Use the arcus tangent of imaginary (q) divided by real (i) to
|
||||
* get the phase angle (+/-) which was directly manipulated by the
|
||||
* original message waveform during the modulation. This value now
|
||||
* serves as the instantaneous amplitude of the demodulated signal
|
||||
*/
|
||||
double denominator = 1.0d / i;
|
||||
angle = Math.atan( (double)q * denominator );
|
||||
}
|
||||
|
||||
Ellipse2D.Double ellipse =
|
||||
new Ellipse2D.Double( centerX - ( i * scale ),
|
||||
centerY - ( q * scale ), 4, 4 );
|
||||
|
||||
graphics.draw( ellipse );
|
||||
|
||||
mCounter -= mSamplesPerSymbol;
|
||||
}
|
||||
|
||||
mCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
public class TimingOffsetItem extends JSlider implements ChangeListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public TimingOffsetItem( int maxValue, int currentValue )
|
||||
{
|
||||
super( JSlider.HORIZONTAL, 0, maxValue, currentValue );
|
||||
|
||||
setMajorTickSpacing( 10 );
|
||||
setMinorTickSpacing( 5 );
|
||||
setPaintTicks( true );
|
||||
setPaintLabels( true );
|
||||
|
||||
addChangeListener( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent event )
|
||||
{
|
||||
int value = ((JSlider)event.getSource()).getValue();
|
||||
|
||||
setOffset( (float)value / 10.0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package io.github.dsheirer.gui.control;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.chart.AreaChart;
|
||||
import javafx.scene.chart.NumberAxis;
|
||||
import javafx.scene.chart.XYChart;
|
||||
import javafx.scene.shape.ClosePath;
|
||||
import javafx.scene.shape.CubicCurveTo;
|
||||
import javafx.scene.shape.LineTo;
|
||||
import javafx.scene.shape.MoveTo;
|
||||
import javafx.scene.shape.Path;
|
||||
import javafx.scene.shape.PathElement;
|
||||
import javafx.util.Pair;
|
||||
|
||||
public class CurveFittedAreaChart extends AreaChart<Number, Number>
|
||||
{
|
||||
|
||||
public CurveFittedAreaChart( NumberAxis xAxis, NumberAxis yAxis )
|
||||
{
|
||||
super( xAxis, yAxis );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutPlotChildren()
|
||||
{
|
||||
super.layoutPlotChildren();
|
||||
for ( int seriesIndex = 0; seriesIndex < getDataSize(); seriesIndex++ )
|
||||
{
|
||||
final XYChart.Series<Number, Number> series = getData()
|
||||
.get( seriesIndex );
|
||||
final Path seriesLine = (Path) ( (Group) series.getNode() )
|
||||
.getChildren().get( 1 );
|
||||
final Path fillPath = (Path) ( (Group) series.getNode() )
|
||||
.getChildren().get( 0 );
|
||||
smooth( seriesLine.getElements(), fillPath.getElements() );
|
||||
}
|
||||
}
|
||||
|
||||
private int getDataSize()
|
||||
{
|
||||
final ObservableList<XYChart.Series<Number, Number>> data = getData();
|
||||
return ( data != null ) ? data.size() : 0;
|
||||
}
|
||||
|
||||
private static void smooth( ObservableList<PathElement> strokeElements,
|
||||
ObservableList<PathElement> fillElements )
|
||||
{
|
||||
// as we do not have direct access to the data, first recreate the list
|
||||
// of all the data points we have
|
||||
final Point2D[] dataPoints = new Point2D[ strokeElements.size() ];
|
||||
for ( int i = 0; i < strokeElements.size(); i++ )
|
||||
{
|
||||
final PathElement element = strokeElements.get( i );
|
||||
if ( element instanceof MoveTo )
|
||||
{
|
||||
final MoveTo move = (MoveTo) element;
|
||||
dataPoints[ i ] = new Point2D( move.getX(), move.getY() );
|
||||
} else if ( element instanceof LineTo )
|
||||
{
|
||||
final LineTo line = (LineTo) element;
|
||||
final double x = line.getX(), y = line.getY();
|
||||
dataPoints[ i ] = new Point2D( x, y );
|
||||
}
|
||||
}
|
||||
// next we need to know the zero Y value
|
||||
final double zeroY = ( (MoveTo) fillElements.get( 0 ) ).getY();
|
||||
// now clear and rebuild elements
|
||||
strokeElements.clear();
|
||||
fillElements.clear();
|
||||
Pair<Point2D[], Point2D[]> result = calcCurveControlPoints(
|
||||
dataPoints );
|
||||
Point2D[] firstControlPoints = result.getKey();
|
||||
Point2D[] secondControlPoints = result.getValue();
|
||||
// start both paths
|
||||
strokeElements.add(
|
||||
new MoveTo( dataPoints[ 0 ].getX(), dataPoints[ 0 ].getY() ) );
|
||||
fillElements.add( new MoveTo( dataPoints[ 0 ].getX(), zeroY ) );
|
||||
fillElements.add(
|
||||
new LineTo( dataPoints[ 0 ].getX(), dataPoints[ 0 ].getY() ) );
|
||||
// add curves
|
||||
for ( int i = 1; i < dataPoints.length; i++ )
|
||||
{
|
||||
final int ci = i - 1;
|
||||
strokeElements
|
||||
.add( new CubicCurveTo( firstControlPoints[ ci ].getX(),
|
||||
firstControlPoints[ ci ].getY(),
|
||||
secondControlPoints[ ci ].getX(),
|
||||
secondControlPoints[ ci ].getY(),
|
||||
dataPoints[ i ].getX(), dataPoints[ i ].getY() ) );
|
||||
fillElements.add( new CubicCurveTo( firstControlPoints[ ci ].getX(),
|
||||
firstControlPoints[ ci ].getY(),
|
||||
secondControlPoints[ ci ].getX(),
|
||||
secondControlPoints[ ci ].getY(), dataPoints[ i ].getX(),
|
||||
dataPoints[ i ].getY() ) );
|
||||
}
|
||||
// end the paths
|
||||
fillElements.add( new LineTo(
|
||||
dataPoints[ dataPoints.length - 1 ].getX(), zeroY ) );
|
||||
fillElements.add( new ClosePath() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate open-ended Bezier Spline Control Points.
|
||||
*/
|
||||
public static Pair<Point2D[], Point2D[]> calcCurveControlPoints(
|
||||
Point2D[] dataPoints )
|
||||
{
|
||||
Point2D[] firstControlPoints;
|
||||
Point2D[] secondControlPoints;
|
||||
int n = dataPoints.length - 1;
|
||||
if ( n == 1 )
|
||||
{ // Special case: Bezier curve should be a straight line.
|
||||
firstControlPoints = new Point2D[ 1 ];
|
||||
// 3P1 = 2P0 + P3
|
||||
firstControlPoints[ 0 ] = new Point2D(
|
||||
( 2 * dataPoints[ 0 ].getX() + dataPoints[ 1 ].getX() ) / 3,
|
||||
( 2 * dataPoints[ 0 ].getY() + dataPoints[ 1 ].getY() )
|
||||
/ 3 );
|
||||
|
||||
secondControlPoints = new Point2D[ 1 ];
|
||||
// P2 = 2P1 – P0
|
||||
secondControlPoints[ 0 ] = new Point2D(
|
||||
2 * firstControlPoints[ 0 ].getX() - dataPoints[ 0 ].getX(),
|
||||
2 * firstControlPoints[ 0 ].getY()
|
||||
- dataPoints[ 0 ].getY() );
|
||||
return new Pair<Point2D[], Point2D[]>( firstControlPoints,
|
||||
secondControlPoints );
|
||||
}
|
||||
|
||||
// Calculate first Bezier control points
|
||||
// Right hand side vector
|
||||
double[] rhs = new double[ n ];
|
||||
|
||||
// Set right hand side X values
|
||||
for ( int i = 1; i < n - 1; ++i )
|
||||
{
|
||||
rhs[ i ] = 4 * dataPoints[ i ].getX()
|
||||
+ 2 * dataPoints[ i + 1 ].getX();
|
||||
}
|
||||
rhs[ 0 ] = dataPoints[ 0 ].getX() + 2 * dataPoints[ 1 ].getX();
|
||||
rhs[ n - 1 ] = ( 8 * dataPoints[ n - 1 ].getX()
|
||||
+ dataPoints[ n ].getX() ) / 2.0;
|
||||
// Get first control points X-values
|
||||
double[] x = GetFirstControlPoints( rhs );
|
||||
|
||||
// Set right hand side Y values
|
||||
for ( int i = 1; i < n - 1; ++i )
|
||||
{
|
||||
rhs[ i ] = 4 * dataPoints[ i ].getY()
|
||||
+ 2 * dataPoints[ i + 1 ].getY();
|
||||
}
|
||||
rhs[ 0 ] = dataPoints[ 0 ].getY() + 2 * dataPoints[ 1 ].getY();
|
||||
rhs[ n - 1 ] = ( 8 * dataPoints[ n - 1 ].getY()
|
||||
+ dataPoints[ n ].getY() ) / 2.0;
|
||||
// Get first control points Y-values
|
||||
double[] y = GetFirstControlPoints( rhs );
|
||||
|
||||
// Fill output arrays.
|
||||
firstControlPoints = new Point2D[ n ];
|
||||
secondControlPoints = new Point2D[ n ];
|
||||
for ( int i = 0; i < n; ++i )
|
||||
{
|
||||
// First control point
|
||||
firstControlPoints[ i ] = new Point2D( x[ i ], y[ i ] );
|
||||
// Second control point
|
||||
if ( i < n - 1 )
|
||||
{
|
||||
secondControlPoints[ i ] = new Point2D(
|
||||
2 * dataPoints[ i + 1 ].getX() - x[ i + 1 ],
|
||||
2 * dataPoints[ i + 1 ].getY() - y[ i + 1 ] );
|
||||
} else
|
||||
{
|
||||
secondControlPoints[ i ] = new Point2D(
|
||||
( dataPoints[ n ].getX() + x[ n - 1 ] ) / 2,
|
||||
( dataPoints[ n ].getY() + y[ n - 1 ] ) / 2 );
|
||||
}
|
||||
}
|
||||
return new Pair<Point2D[], Point2D[]>( firstControlPoints,
|
||||
secondControlPoints );
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves a tridiagonal system for one of coordinates (x or y) of first
|
||||
* Bezier control points.
|
||||
*/
|
||||
private static double[] GetFirstControlPoints( double[] rhs )
|
||||
{
|
||||
int n = rhs.length;
|
||||
double[] x = new double[ n ]; // Solution vector.
|
||||
double[] tmp = new double[ n ]; // Temp workspace.
|
||||
double b = 2.0;
|
||||
x[ 0 ] = rhs[ 0 ] / b;
|
||||
for ( int i = 1; i < n; i++ )
|
||||
{// Decomposition and forward substitution.
|
||||
tmp[ i ] = 1 / b;
|
||||
b = ( i < n - 1 ? 4.0 : 3.5 ) - tmp[ i ];
|
||||
x[ i ] = ( rhs[ i ] - x[ i - 1 ] ) / b;
|
||||
}
|
||||
for ( int i = 1; i < n; i++ )
|
||||
{
|
||||
x[ n - i - 1 ] -= tmp[ n - i ] * x[ n - i ]; // Backsubstitution.
|
||||
}
|
||||
return x;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.gui.control;
|
||||
|
||||
import io.github.dsheirer.source.InvalidFrequencyException;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
|
||||
import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event;
|
||||
import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProcessor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.event.MouseWheelListener;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class JFrequencyControl extends JPanel implements IFrequencyChangeProcessor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger(JFrequencyControl.class);
|
||||
|
||||
private List<IFrequencyChangeProcessor> mProcessors = new ArrayList<>();
|
||||
|
||||
private Color mHighlightColor = Color.YELLOW;
|
||||
|
||||
private long mFrequency;
|
||||
|
||||
private Cursor mBlankCursor;
|
||||
|
||||
private HashMap<Integer, Digit> mDigits = new HashMap<Integer, Digit>();
|
||||
|
||||
public JFrequencyControl()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
public JFrequencyControl(long value)
|
||||
{
|
||||
this();
|
||||
mFrequency = value;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[]0[]", ""));
|
||||
|
||||
Font font = new Font(Font.MONOSPACED, Font.BOLD, 30);
|
||||
|
||||
for (int x = 9; x >= 0; x--)
|
||||
{
|
||||
Digit digit = null;
|
||||
|
||||
try
|
||||
{
|
||||
digit = new Digit(x);
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
mLog.error("JFrequencyControl - parse exception "
|
||||
+ "constructing a digit - " + e);
|
||||
}
|
||||
|
||||
if (digit != null)
|
||||
{
|
||||
mDigits.put(x, digit);
|
||||
|
||||
add(digit);
|
||||
|
||||
digit.setFont(font);
|
||||
|
||||
if (x == 6)
|
||||
{
|
||||
JLabel period = new JLabel(".");
|
||||
|
||||
add(period);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(new JLabel(" MHz"));
|
||||
|
||||
/**
|
||||
* Create a blank cursor to use when the mouse is over the digits
|
||||
*/
|
||||
//Create an empty byte array
|
||||
byte[] imageByte = new byte[0];
|
||||
|
||||
//Create image for cursor using empty array
|
||||
Image cursorImage = Toolkit.getDefaultToolkit().createImage(imageByte);
|
||||
|
||||
mBlankCursor = Toolkit.getDefaultToolkit()
|
||||
.createCustomCursor(cursorImage, new Point(0, 0), "cursor");
|
||||
|
||||
revalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled)
|
||||
{
|
||||
super.setEnabled(enabled);
|
||||
|
||||
for (Digit digit : mDigits.values())
|
||||
{
|
||||
digit.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a frequency change event invoked by another control. We don't
|
||||
* rebroadcast this event, just set the control to indicate the new frequency.
|
||||
*/
|
||||
@Override
|
||||
public void frequencyChanged(FrequencyChangeEvent event)
|
||||
{
|
||||
if (event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE)
|
||||
{
|
||||
setFrequency(event.getValue().longValue(), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the frequency into the display and optionally fires a change event
|
||||
* to all registered listeners
|
||||
*/
|
||||
public void setFrequency(long frequency, boolean fireChangeEvent)
|
||||
{
|
||||
mFrequency = frequency;
|
||||
|
||||
for (Digit digit : mDigits.values())
|
||||
{
|
||||
digit.setFrequency(frequency, fireChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public long getFrequency()
|
||||
{
|
||||
return mFrequency;
|
||||
}
|
||||
|
||||
private void updateFrequency()
|
||||
{
|
||||
long frequency = 0;
|
||||
|
||||
for (Digit digit : mDigits.values())
|
||||
{
|
||||
frequency += digit.getFrequency();
|
||||
}
|
||||
|
||||
mFrequency = frequency;
|
||||
}
|
||||
|
||||
private void fireFrequencyChanged() throws SourceException
|
||||
{
|
||||
updateFrequency();
|
||||
|
||||
Iterator<IFrequencyChangeProcessor> it = mProcessors.iterator();
|
||||
|
||||
FrequencyChangeEvent event = new FrequencyChangeEvent(
|
||||
Event.REQUEST_FREQUENCY_CHANGE, mFrequency);
|
||||
|
||||
while (it.hasNext())
|
||||
{
|
||||
it.next().frequencyChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(IFrequencyChangeProcessor processor)
|
||||
{
|
||||
mProcessors.add(processor);
|
||||
}
|
||||
|
||||
public void removeListener(IFrequencyChangeProcessor processor)
|
||||
{
|
||||
mProcessors.remove(processor);
|
||||
}
|
||||
|
||||
public class Digit extends JTextField
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private int mPower = 0;
|
||||
private long mValue = 0;
|
||||
|
||||
private Digit(int position) throws ParseException
|
||||
{
|
||||
super("0");
|
||||
|
||||
mPower = position;
|
||||
|
||||
Listener listener = new Listener();
|
||||
|
||||
this.addKeyListener(listener);
|
||||
this.addMouseListener(listener);
|
||||
this.addMouseWheelListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this digit to the value of the column in frequency that corresponds
|
||||
* to the power (ie column) set for this digit. Optionally, fires a
|
||||
* value change event to all listeners.
|
||||
*/
|
||||
public void setFrequency(long frequency, boolean fireChangeEvent)
|
||||
{
|
||||
//Strip the digits higher than this one
|
||||
long lower = frequency % (long) (Math.pow(10, mPower + 1));
|
||||
|
||||
//Set the value to int value of dividing by 10 to this power
|
||||
long value = (long) (lower / (long) (Math.pow(10, mPower)));
|
||||
|
||||
set(value, fireChangeEvent);
|
||||
}
|
||||
|
||||
public long getFrequency()
|
||||
{
|
||||
return mValue * (long) Math.pow(10, mPower);
|
||||
}
|
||||
|
||||
public void increment()
|
||||
{
|
||||
increment(true);
|
||||
}
|
||||
|
||||
public void increment(boolean fireChangeEvent)
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
set(mValue + 1, fireChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public void decrement()
|
||||
{
|
||||
decrement(true);
|
||||
}
|
||||
|
||||
public void decrement(boolean fireChangeEvent)
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
set(mValue - 1, fireChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper to change amount and fire change event
|
||||
*/
|
||||
private void set(long amount)
|
||||
{
|
||||
set(amount, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the value and optionally fires change event to listeners
|
||||
*/
|
||||
private void set(long amount, boolean fireChangeEvent)
|
||||
{
|
||||
long previous = mValue;
|
||||
boolean higherDigitIncremented = false;
|
||||
boolean higherDigitDecremented = false;
|
||||
|
||||
mValue = amount;
|
||||
|
||||
|
||||
while (mValue < 0)
|
||||
{
|
||||
mValue += 10;
|
||||
|
||||
Digit nextHigherDigit = mDigits.get(mPower + 1);
|
||||
|
||||
if (nextHigherDigit != null)
|
||||
{
|
||||
nextHigherDigit.decrement(false);
|
||||
higherDigitDecremented = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (mValue > 9)
|
||||
{
|
||||
mValue -= 10;
|
||||
|
||||
Digit nextHigherDigit = mDigits.get(mPower + 1);
|
||||
|
||||
if (nextHigherDigit != null)
|
||||
{
|
||||
nextHigherDigit.increment(false);
|
||||
higherDigitIncremented = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fireChangeEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
fireFrequencyChanged();
|
||||
}
|
||||
catch (SourceException se)
|
||||
{
|
||||
mValue = previous;
|
||||
|
||||
if (higherDigitIncremented)
|
||||
{
|
||||
Digit nextHigherDigit = mDigits.get(mPower + 1);
|
||||
|
||||
if (nextHigherDigit != null)
|
||||
{
|
||||
nextHigherDigit.decrement(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (higherDigitDecremented)
|
||||
{
|
||||
Digit nextHigherDigit = mDigits.get(mPower + 1);
|
||||
|
||||
if (nextHigherDigit != null)
|
||||
{
|
||||
nextHigherDigit.increment(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (se instanceof InvalidFrequencyException)
|
||||
{
|
||||
InvalidFrequencyException ife = (InvalidFrequencyException) se;
|
||||
|
||||
JOptionPane.showMessageDialog(this, "Frequency [" + ife.getInvalidFrequency() +
|
||||
"] exceeds the frequency limit [" + ife.getValidFrequency() + "] for this tuner.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setText(String.valueOf(mValue));
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
private class Listener implements KeyListener,
|
||||
MouseListener,
|
||||
MouseWheelListener
|
||||
{
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e)
|
||||
{
|
||||
int key = e.getKeyCode();
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case KeyEvent.VK_0:
|
||||
case KeyEvent.VK_NUMPAD0:
|
||||
set(0);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_1:
|
||||
case KeyEvent.VK_NUMPAD1:
|
||||
set(1);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_2:
|
||||
case KeyEvent.VK_NUMPAD2:
|
||||
set(2);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_3:
|
||||
case KeyEvent.VK_NUMPAD3:
|
||||
set(3);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_4:
|
||||
case KeyEvent.VK_NUMPAD4:
|
||||
set(4);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_5:
|
||||
case KeyEvent.VK_NUMPAD5:
|
||||
set(5);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_6:
|
||||
case KeyEvent.VK_NUMPAD6:
|
||||
set(6);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_7:
|
||||
case KeyEvent.VK_NUMPAD7:
|
||||
set(7);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_8:
|
||||
case KeyEvent.VK_NUMPAD8:
|
||||
set(8);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_9:
|
||||
case KeyEvent.VK_NUMPAD9:
|
||||
set(9);
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_LEFT:
|
||||
Digit.this.transferFocusBackward();
|
||||
break;
|
||||
case KeyEvent.VK_RIGHT:
|
||||
Digit.this.transferFocus();
|
||||
break;
|
||||
case KeyEvent.VK_UP:
|
||||
increment();
|
||||
break;
|
||||
case KeyEvent.VK_DOWN:
|
||||
decrement();
|
||||
break;
|
||||
case KeyEvent.VK_TAB:
|
||||
break;
|
||||
default:
|
||||
set(mValue);
|
||||
break;
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e)
|
||||
{
|
||||
switch (e.getButton())
|
||||
{
|
||||
case MouseEvent.BUTTON1:
|
||||
increment();
|
||||
break;
|
||||
case MouseEvent.BUTTON2:
|
||||
break;
|
||||
case MouseEvent.BUTTON3:
|
||||
decrement();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
Digit.this.setBackground(mHighlightColor);
|
||||
|
||||
setCursor(mBlankCursor);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
Digit.this.setBackground(Color.WHITE);
|
||||
|
||||
setCursor(Cursor.getDefaultCursor());
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(MouseWheelEvent e)
|
||||
{
|
||||
set(mValue - e.getWheelRotation());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user