package Code.graphics.morseCode;
/*
* MorseCodeFrame.java
*
* Created on Jan 12, 2009, 6:36:27 PM
*/
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.Timer;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.layout.GroupLayout;
import org.jdesktop.layout.LayoutStyle;
/**
*
* @author Erik Pasternak
*
* Most of this code was created automatically by Java NetBeans IDE.
*/
@SuppressWarnings({"UseOfSystemOutOrSystemErr"})
final class MorseCodeFrame extends JFrame implements ActionListener
{
//These were added by me to let me differentiate between the button and the
//tail being pressed.
public static final int BUTTON = 0;
public static final int TAIL = 1;
/** Creates new form MorseCodeFrame */
MorseCodeFrame()
{
initComponents();
new Timer(10, this).start();
}
//This adds a letter to the message box by looking up the morse code in
//the morse code box to find the appropriate letter.
public void addLetter()
{
//Get the current string of morse code
String currLetter = morseText.getText();
//if it's not empty
if (currLetter.length() > 0)
{
//clear the morse code box
morseText.setText("");
//trim what we have to make sure there's no whitespace
currLetter = currLetter.trim();
//set a flag that we haven't found it yet
boolean letterFound = false;
//Go through the lookup array for morse code strings
for (int i = 0; i < MorseCodeBeeper.MorseLookup.length; i++)
{
//If we find it add it to the message and break
if (currLetter.compareTo(MorseCodeBeeper.MorseLookup[i]) == 0)
{
messageText.append(MorseCodeBeeper.asciiLookup[i]);
letterFound = true;
break;
}
}
//if we didn't find it give some output
if (!letterFound)
{
System.out.printf("%s was not recognized and will be ignored\n", currLetter);
}
}
}
/** This method should be called everytime a change in the state
* of the beep occurs. This can either be caused by the finch
* crossing its threshhold for beeping or from the beep button on
* the GUI being pressed/unpressed.
*/
public void beepStateChanged(boolean pressed, final int pressSrc)
{
//We want the gui button to take precedence, since it is not pressed by
//default.
if (this.buttonPressed)
{
pressed = true;
}
if (pressed)
{
pressCount++;
nPressCount = 0;
}
else
{
nPressCount++;
pressCount = 0;
}
//keeping a count lets us filter out the bouncing in the tail but introduces
//about 60-100 ms of delay
if (pressed != prevBeepPressed && ((pressCount > 3 || nPressCount > 3) || pressSrc == BUTTON))
{
//Set up some variables for figuring out what to do because of the
//transition
final int thresh1;
final int thresh2;
final int threshsp1;
//We still want to ignore very short things because they are usually
//the tail bouncing
final int ignoreThresh = 50; //dotLength/4;
final double oldTime = startTime;
//Find out how long since we last changed states
final double timeDiff = System.currentTimeMillis() - oldTime;
//if it wasn't too short restart the timer
if (timeDiff > ignoreThresh)
{
startTime = System.currentTimeMillis();
}
//These set up how closely we follow the standard morse restrictions
//on timing. The not strict threshholds mostly apply to the wait time
//after the button was released.
if (strictTimingBox.isEnabled())
{
thresh1 = oneThreeThresh;
threshsp1 = oneThreeThresh;
thresh2 = threeSevenThresh;
}
else
{
thresh1 = oneThreeThresh;
threshsp1 = notStrict13Thresh;
thresh2 = notStrict37Thresh;
}
//a dash is three dots
//a space between dots and dashes is one dot
//a space between letters is 3 dots
//a space between words is 7 dots
//If we just let go of the button
if (prevBeepPressed && !pressed)
{
//if it was too short a time amount ignore it.
if (timeDiff < ignoreThresh)
{
}
//if it was a dot length output a dot
else if (timeDiff < thresh1)
{
morseText.setText(morseText.getText() + ".");
}
//for a dash length output a dash
else if (timeDiff < thresh2)
{
morseText.setText(morseText.getText() + "-");
}
}
//if we just pressed the button
else if (!prevBeepPressed && pressed)
{
//Don't do anything, we only add things when we let go of the button.
if (timeDiff < threshsp1)
{
}
else if (timeDiff < thresh2)
{
}
else
{
}
}
//if we did something change the previous state
if (timeDiff > ignoreThresh)
{
prevBeepPressed = pressed;
}
}
}
//This is an interrupt that fires every 10ms and performs updates on the output
public void actionPerformed(final ActionEvent e)
{
final int thresh1;
final int thresh2;
final int threshsp1;
//We'd like to know how long since our current state started
final double timeDiff = System.currentTimeMillis() - startTime;
//Again, we set different threshholds depending on how strict we're being
if (strictTimingBox.isEnabled())
{
thresh1 = oneThreeThresh;
threshsp1 = oneThreeThresh;
thresh2 = threeSevenThresh;
}
else
{
thresh1 = oneThreeThresh;
threshsp1 = notStrict13Thresh;
thresh2 = notStrict37Thresh;
}
//If we just started the program set the start time
if (startTime == 0)
{
startTime = System.currentTimeMillis();
}
//if the button is not currently being pressed
if (!prevBeepPressed)
{
//And we're below the time for a space between dots
if (timeDiff < threshsp1)
{
//just update the output bar
typeTimingBar.setValue((int)(100 * timeDiff / thresh1));
typeTimingBar.setString("dot-space");
}
//if we've reached a letter space timing
else if (timeDiff < thresh2)
{
//update the output bar
typeTimingBar.setValue((int)(100 * (timeDiff - thresh1) / (thresh2 - thresh1)));
typeTimingBar.setString("letter-space");
//and add the letter if it exists
addLetter();
}
//if we're above that it's a word space
else
{
//if we haven't already add a space to the message
typeTimingBar.setValue(100);
typeTimingBar.setString("word-space");
if (!messageText.getText().endsWith(" "))
{
messageText.append(" ");
}
}
}
//If it is currently pressed
else
{
//We just update the output appropriately
if (timeDiff < thresh1)
{
typeTimingBar.setValue((int)(100 * timeDiff / thresh1));
typeTimingBar.setString("dot");
}
else if (timeDiff < thresh2)
{
typeTimingBar.setValue((int)(100 * (timeDiff - thresh1) / (thresh2 - thresh1)));
typeTimingBar.setString("dash");
}
else if (timeDiff < backspaceThresh)
{
typeTimingBar.setValue((int)(100 * timeDiff - thresh2) / (backspaceThresh - thresh2));
typeTimingBar.setString("nothing");
}
//If they hold it down long enough start deleting chars from the message
else if (timeDiff > backspaceThresh)
{
typeTimingBar.setValue(100);
typeTimingBar.setString("backspace");
if (backspaceCount == 0)
{
final int msgLength = messageText.getText().length() - 2;
if (msgLength >= 0)
{
messageText.setText(messageText.getText().substring(0, msgLength));
}
}
//and clear any morse they might have input
morseText.setText("");
backspaceCount = (++backspaceCount) % 10;
}
}
}
/////The code below here is autogenerated by NetBeans///////////////////////////
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents()
{
startTime = 0;
prevBeepPressed = false;
dotLength = 800;
oneThreeThresh = 2 * dotLength;
threeSevenThresh = 6 * dotLength;
notStrict13Thresh = 6 * dotLength;
notStrict37Thresh = 12 * dotLength;
backspaceThresh = 20 * dotLength;
backspaceCount = 0;
pressCount = 0;
nPressCount = 0;
jScrollPane1 = new JScrollPane();
messageText = new JTextArea();
morseText = new JTextField();
beepButton = new JButton();
jScrollPane2 = new JScrollPane();
directionsText = new JTextArea();
jLabel1 = new JLabel();
jLabel2 = new JLabel();
strictTimingBox = new JCheckBox();
dotLengthSlider = new JSlider();
dotLengthLabel = new JLabel();
freqLabel = new JLabel();
freqSlider = new JSlider();
typeTimingBar = new JProgressBar();
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
messageText.setColumns(20);
messageText.setEditable(false);
messageText.setRows(5);
jScrollPane1.setViewportView(messageText);
messageText.getAccessibleContext().setAccessibleName("morseMessageBox");
morseText.setEditable(false);
morseText.setFont(new Font("Bookman Old Style", 1, 18));
morseText.setHorizontalAlignment(JTextField.CENTER);
morseText.setText(".-.---.");
morseText.setName("MorseLetter"); // NOI18N
beepButton.setBackground(new Color(40, 0, 200));
beepButton.setText("Beep");
beepButton.setName("BeepIndicator"); // NOI18N
beepButton.addMouseListener(new MouseAdapter()
{
public void mousePressed(final MouseEvent evt)
{
beepButtonMousePressed(evt);
}
public void mouseReleased(final MouseEvent evt)
{
beepButtonMouseReleased(evt);
}
});
/*
beepButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
beepButtonActionPerformed(evt);
}
});
beepButton.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
public void propertyChange(java.beans.PropertyChangeEvent evt) {
beepButtonPropertyChange(evt);
}
});
beepButton.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
beepButtonStateChanged(evt);
}
});
*/
jScrollPane2.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
directionsText.setColumns(20);
directionsText.setEditable(false);
directionsText.setFont(new Font("Arial", 0, 10));
directionsText.setLineWrap(true);
directionsText.setRows(5);
directionsText.setText("Use the finch or the beep button to input morse code. What you're inputting will appear in the bottom box as dots and dashes and the translated message will appear in the center left box. Hold down beep to backspace. Strict Timing will enforce standard morse timing on input.");
directionsText.setWrapStyleWord(true);
jScrollPane2.setViewportView(directionsText);
jLabel1.setText("Message");
jLabel2.setText("Current Character");
strictTimingBox.setText("Strict Timing");
strictTimingBox.addActionListener(new ActionListener()
{
public void actionPerformed(final ActionEvent evt)
{
strictTimingBoxActionPerformed(evt);
}
});
dotLengthSlider.setMajorTickSpacing(50);
dotLengthSlider.setMaximum(1000);
dotLengthSlider.setMinimum(100);
dotLengthSlider.setPaintTicks(true);
dotLengthSlider.setValue(dotLength);
dotLengthSlider.addPropertyChangeListener(new PropertyChangeListener()
{
public void propertyChange(final PropertyChangeEvent evt)
{
dotLengthSliderPropertyChange(evt);
}
});
dotLengthSlider.addChangeListener(new ChangeListener()
{
public void stateChanged(final ChangeEvent evt)
{
dotLengthSliderStateChanged(evt);
}
});
dotLengthLabel.setText("Dot Length: 100ms");
freqLabel.setText("Beep Frequency: 700Hz");
freqSlider.setMaximum(10000);
freqSlider.setMinimum(20);
freqSlider.addPropertyChangeListener(new PropertyChangeListener()
{
public void propertyChange(final PropertyChangeEvent evt)
{
freqSliderPropertyChange(evt);
}
});
freqSlider.addChangeListener(new ChangeListener()
{
public void stateChanged(final ChangeEvent evt)
{
freqSliderStateChanged(evt);
}
});
typeTimingBar.setName("letter space"); // NOI18N
typeTimingBar.setString("letter space");
typeTimingBar.setStringPainted(true);
final GroupLayout layout = new GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(GroupLayout.LEADING)
.add(jScrollPane2, GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE)
.add(layout.createSequentialGroup()
.add(layout.createParallelGroup(GroupLayout.CENTER)
.add(morseText, GroupLayout.DEFAULT_SIZE, 224, Short.MAX_VALUE)
.add(jScrollPane1, GroupLayout.PREFERRED_SIZE, 224, GroupLayout.PREFERRED_SIZE)
.add(jLabel1)
.add(jLabel2))
.addPreferredGap(LayoutStyle.RELATED)
.add(layout.createParallelGroup(GroupLayout.LEADING)
.add(GroupLayout.CENTER, dotLengthLabel)
.add(GroupLayout.CENTER, dotLengthSlider, 0, 0, Short.MAX_VALUE)
.add(GroupLayout.CENTER, freqLabel)
.add(GroupLayout.CENTER, freqSlider, GroupLayout.PREFERRED_SIZE, 150, GroupLayout.PREFERRED_SIZE)
.add(layout.createSequentialGroup()
.add(beepButton, GroupLayout.PREFERRED_SIZE, 64, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.RELATED)
.add(strictTimingBox))
.add(typeTimingBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(jScrollPane2, GroupLayout.PREFERRED_SIZE, 54, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.RELATED)
.add(layout.createParallelGroup(GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.add(2, 2, 2)
.add(jLabel1)
.add(2, 2, 2)
.add(jScrollPane1, GroupLayout.PREFERRED_SIZE, 106, GroupLayout.PREFERRED_SIZE))
.add(layout.createSequentialGroup()
.add(dotLengthLabel)
.add(1, 1, 1)
.add(dotLengthSlider, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.UNRELATED)
.add(freqLabel)
.add(1, 1, 1)
.add(freqSlider, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)))
.add(10, 10, 10)
.add(jLabel2)
.addPreferredGap(LayoutStyle.RELATED)
.add(layout.createParallelGroup(GroupLayout.TRAILING)
.add(layout.createSequentialGroup()
.add(morseText, GroupLayout.PREFERRED_SIZE, 42, GroupLayout.PREFERRED_SIZE)
.add(24, 24, 24))
.add(layout.createSequentialGroup()
.add(layout.createParallelGroup(GroupLayout.BASELINE)
.add(beepButton, GroupLayout.DEFAULT_SIZE, 31, Short.MAX_VALUE)
.add(strictTimingBox))
.addPreferredGap(LayoutStyle.RELATED)
.add(typeTimingBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addContainerGap())))
);
morseText.getAccessibleContext().setAccessibleName("morseMorseBox");
dotLengthSlider.getAccessibleContext().setAccessibleName("dotLengthSlider");
dotLengthLabel.getAccessibleContext().setAccessibleName("Tick Length: 100ms");
pack();
}// </editor-fold>
private void dotLengthSliderPropertyChange(final PropertyChangeEvent evt)
{
dotLengthLabel.setText("Dot Length: " + this.dotLengthSlider.getValue() + "ms");
}
private void dotLengthSliderStateChanged(final ChangeEvent evt)
{
dotLengthLabel.setText("Dot Length: " + this.dotLengthSlider.getValue() + "ms");
dotLength = dotLengthSlider.getValue();
oneThreeThresh = 2 * dotLength;
threeSevenThresh = 6 * dotLength;
notStrict13Thresh = 6 * dotLength;
notStrict37Thresh = 12 * dotLength;
backspaceThresh = 20 * dotLength;
}
private void strictTimingBoxActionPerformed(final ActionEvent evt)
{
// TODO add your handling code here:
}
private void freqSliderPropertyChange(final PropertyChangeEvent evt)
{
freqLabel.setText("Beep Frequency: " + freqSlider.getValue() + "Hz");
}
private void freqSliderStateChanged(final ChangeEvent evt)
{
freqLabel.setText("Beep Frequency: " + freqSlider.getValue() + "Hz");
}
private void beepButtonMousePressed(final MouseEvent evt)
{
beepStateChanged(true, BUTTON);
this.buttonPressed = true;
}
private void beepButtonMouseReleased(final MouseEvent evt)
{
beepStateChanged(false, BUTTON);
this.buttonPressed = false;
}
public int getFreq()
{
return freqSlider.getValue();
}
/**
* @param args the command line arguments
*/
public static void main(final String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
new MorseCodeFrame().setVisible(true);
}
});
}
// Variables declaration - do not modify
private JButton beepButton;
private JTextArea directionsText;
private JLabel dotLengthLabel;
private JSlider dotLengthSlider;
private JLabel freqLabel;
private JSlider freqSlider;
private JLabel jLabel1;
private JLabel jLabel2;
private JScrollPane jScrollPane1;
private JScrollPane jScrollPane2;
private JTextArea messageText;
private JTextField morseText;
private JCheckBox strictTimingBox;
private JProgressBar typeTimingBar;
// End of auto-generated variables declaration
//These are extra variables I've added in to help track things.
private double startTime;
private boolean prevBeepPressed;
//private boolean prevPrevBeepPressed;
private int dotLength;
private int oneThreeThresh;
private int threeSevenThresh;
private int notStrict13Thresh;
private int notStrict37Thresh;
private int backspaceThresh;
private int backspaceCount;
private int pressCount;
private int nPressCount;
//this keeps track of whether or not the gui button is being pressed
private boolean buttonPressed = false;
}