// // // // VFR Flight Log // // // Version Date Modification // ------- --------- ---------------------------------------------------- // 00.95 24 mar 08 add automatic file selection to file dialogs // // // package com.knutejohnson.tools.aviation.vfrlog; import java.awt.*; import java.awt.event.*; import java.awt.print.*; import java.io.*; import java.text.*; import java.util.*; import java.util.regex.*; import javax.swing.*; import javax.swing.text.*; import com.knutejohnson.classes.*; public final class VFRFlightLog extends JPanel implements ActionListener { static final String VERSION = "00.95"; static final int LEGS = 16; static final String ABOUT = "VFR Flight Log\nVersion: " + VERSION + "\nCopyright \u00a9 2008 Knute Johnson. All rights reserved.\n" + "rabbitbrush.frazmtn.com/aviation"; static final String PRINT_LABEL = "VFR Flight Log - Version: " + VERSION + " - Copyright \u00a9 2008 Knute Johnson. All rights reserved. " + "rabbitbrush.frazmtn.com/aviation"; static final double TWO_PI = Math.PI * 2.0; JTextField fromField; ArrayList toField = new ArrayList(); ArrayList altField = new ArrayList(); ArrayList tempField = new ArrayList(); ArrayList windField = new ArrayList(); ArrayList tasField = new ArrayList(); ArrayList tcField = new ArrayList(); ArrayList varField = new ArrayList(); JLabel[] mcLabel = new JLabel[LEGS]; JLabel[] wcaLabel = new JLabel[LEGS]; JLabel[] mhLabel = new JLabel[LEGS]; ArrayList devField = new ArrayList(); JLabel[] chLabel = new JLabel[LEGS]; ArrayList distField = new ArrayList(); JLabel[] gsLabel = new JLabel[LEGS]; JLabel[] eteLabel = new JLabel[LEGS]; ArrayList fuelFlowField = new ArrayList(); JLabel[] fuelBurnLabel = new JLabel[LEGS]; JLabel totalDistLabel; JLabel totalTimeLabel; JLabel totalBurnLabel; JLabel l; File file; public VFRFlightLog() { setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.weightx = 1.0; c.insets = new Insets(2,2,2,2); c.gridx = c.gridy = 0; c.gridwidth = 16; l = new JLabel("VFR Flight Log",JLabel.CENTER); l.setFont(l.getFont().deriveFont(24.0f)); add(l,c); ++c.gridy; c.gridwidth = 1; fromField = new JTextField(new FixedLengthDocument(12),"From",10); fromField.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent ke) { int code = ke.getKeyCode(); if (code == KeyEvent.VK_DOWN) toField.get(0).requestFocusInWindow(); else if (code == KeyEvent.VK_UP) getToolkit().beep(); } }); fromField.setToolTipText("Waypoint From"); add(fromField,c); ++c.gridx; l = new JLabel("ALT",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("TEM",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("WIND",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("TAS",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("TC",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("VAR",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("MC",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("WCA",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("MH",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("DEV",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("CH",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("GS",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("DIS",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("ETE",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("FF/HR",JLabel.CENTER); add(l,c); ++c.gridx; l = new JLabel("BURN",JLabel.CENTER); add(l,c); Dimension tcSize = new Dimension(0,0); for (int i=0; i 360) throw new NumberFormatException(); tc = Math.toRadians(tc); } catch (NumberFormatException nfe) { tcField.get(i).requestFocusInWindow(); throw new NumberFormatException("TC"); } double var; try { var = parseVariation(varField.get(i).getText()); } catch (NumberFormatException nfe) { varField.get(i).requestFocusInWindow(); throw new NumberFormatException("VAR"); } String devStr = devField.get(i).getText(); if (devStr.equals("")) devStr = "0"; double dev; try { dev = Math.toRadians(Double.parseDouble(devStr)); } catch (NumberFormatException nfe) { devField.get(i).requestFocusInWindow(); throw new NumberFormatException("DEV"); } double mc = tc + var; if (mc < 0) mc += TWO_PI; mc %= TWO_PI; /* mcLabel[i].setText(String.format("%03.0f", Math.toDegrees(mc))); */ mcLabel[i].setText(formatDegrees(mc)); double wta = tc - windDir; double wca = Math.asin(windVel * Math.sin(wta) / tas); wcaLabel[i].setText(formatWCA(wca)); double mh = mc + wca; if (mh < 0) mh += TWO_PI; mh %= TWO_PI; /* mhLabel[i].setText(String.format("%03.0f", Math.toDegrees(mh))); */ mhLabel[i].setText(formatDegrees(mh)); double ch = mh + dev; if (ch < 0) ch += TWO_PI; ch %= TWO_PI; /* chLabel[i].setText(String.format("%03.0f", Math.toDegrees(ch))); */ chLabel[i].setText(formatDegrees(ch)); double gs = tas * Math.cos(wca) + windVel * Math.cos(wta); gsLabel[i].setText(String.format("%3.0f",gs)); String distStr = distField.get(i).getText(); if (distStr.equals("")) distStr = "0"; double dist; try { dist = Double.parseDouble(distStr); totalDist += dist; } catch (NumberFormatException nfe) { distField.get(i).requestFocusInWindow(); throw new NumberFormatException("DIST"); } if (dist > 0) { int ete = (int)Math.round(dist / gs * 60.0); eteLabel[i].setText(formatTime(ete)); totalTime += ete; try { String flowStr = fuelFlowField.get(i).getText(); if (flowStr.equals("")) flowStr = "0"; double flow = Double.parseDouble(flowStr); double burn = flow * ete / 60.0; fuelBurnLabel[i].setText( String.format("%4.1f",burn)); totalBurn += burn; } catch (NumberFormatException nfe) { fuelFlowField.get(i).requestFocusInWindow(); throw new NumberFormatException("FF/HR"); } } } catch (NumberFormatException nfe) { JOptionPane.showMessageDialog(this, toField.get(i).getText() + " Leg Has Bad Data in " + nfe.getMessage() + " Field", "ERROR",JOptionPane.ERROR_MESSAGE); break; } } } totalDistLabel.setText(String.format("%3.0f",totalDist)); totalTimeLabel.setText(formatTime((int)Math.round(totalTime))); totalBurnLabel.setText(String.format("%5.1f",totalBurn)); } else if (ac.equals("ALT")) { JTextField field = (JTextField)ae.getSource(); String text = field.getText(); int index = altField.indexOf(field); for (int i=index+1; i= 1 && dir <= 36) || (dir > 50 && dir <= 86))) throw new NumberFormatException(); windVel = Double.parseDouble(wind.substring(2,4)); if (dir == 99) dir = 0; if (dir > 36) { dir -= 50; windVel += 100.0; } windDir = Math.toRadians(dir * 10.0 + 180.0); return new double[] { windDir, windVel }; } double parseVariation(String varStr) throws NumberFormatException { double var; Pattern p = Pattern.compile("(-?[\\d]+)([EW]?)"); Matcher m = p.matcher(varStr.toUpperCase()); if (m.matches()) { var = Math.toRadians(Double.parseDouble(m.group(1))); if (m.groupCount() == 2 && m.group(2).equals("E")) var = -var; } else throw new NumberFormatException("Invalid format"); return var; } String formatWCA(double wca) { String WCA; double degrees = Math.toDegrees(wca); if (degrees > 0) WCA = String.format("%2.0f",degrees) + "R"; else WCA = String.format("%2.0f",Math.abs(degrees)) + "L"; return WCA; } String formatDegrees(double hdg) { String str = String.format("%03.0f",Math.toDegrees(hdg)); if (str.equals("000")) str = "360"; return str; } String formatTime(int mins) { int hours = mins / 60; int minutes = mins % 60; return String.format("%02d:%02d",hours,minutes); } static class ArrowListener extends KeyAdapter { final ArrayList list; public ArrowListener(ArrayList list) { this.list = list; } public void keyPressed(KeyEvent ke) { JTextField field = (JTextField)ke.getSource(); int key = ke.getKeyCode(); if (key == KeyEvent.VK_UP) { int index = list.indexOf(field); try { field = list.get(index - 1); field.requestFocusInWindow(); } catch (IndexOutOfBoundsException ioobe) { Toolkit.getDefaultToolkit().beep(); } } else if (key == KeyEvent.VK_DOWN) { int index = list.indexOf(field); try { field = list.get(index + 1); field.requestFocusInWindow(); } catch (IndexOutOfBoundsException ioobe) { Toolkit.getDefaultToolkit().beep(); } } } } static class LogFileFilter extends javax.swing.filechooser.FileFilter { public boolean accept(File file) { return file.isDirectory() || file.getName().endsWith(".flg"); } public String getDescription() { return "VFR Flight Log Files (.flg)"; } } /* static class WindDocument extends PlainDocument { private int length = 0; public WindDocument(int length) { this.length = length; } public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if (str == null) return; str = str.replaceAll("[^/0-9]",""); if (length > 0) if (str.length() + getLength() > length) str = str.substring(0,length - getLength()); super.insertString(offs,str,a); } } */ static class VariationDocument extends PlainDocument { private int length = 0; public VariationDocument(int length) { this.length = length; } public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if (str == null) return; str = str.toUpperCase().replaceAll("[^-+EW0-9]",""); if (length > 0) if (str.length() + getLength() > length) str = str.substring(0,length - getLength()); super.insertString(offs,str,a); } } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { final JFrame f = new JFrame("VFR Flight Log"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); VFRFlightLog log = new VFRFlightLog(); JScrollPane sp = new JScrollPane(log); f.add(sp,BorderLayout.CENTER); JMenuBar mb = new JMenuBar(); f.setJMenuBar(mb); JMenu file = new JMenu("File"); JMenu edit = new JMenu("Edit"); JMenu help = new JMenu("Help"); mb.add(file); mb.add(edit); mb.add(help); file.setMnemonic(KeyEvent.VK_F); edit.setMnemonic(KeyEvent.VK_E); help.setMnemonic(KeyEvent.VK_H); JMenuItem mi = new JMenuItem("Load"); mi.setMnemonic(KeyEvent.VK_L); mi.addActionListener(log); file.add(mi); mi = new JMenuItem("Save"); mi.setMnemonic(KeyEvent.VK_S); mi.addActionListener(log); file.add(mi); file.add(new JSeparator()); mi = new JMenuItem("Print"); mi.setMnemonic(KeyEvent.VK_P); mi.addActionListener(log); file.add(mi); file.add(new JSeparator()); mi = new JMenuItem("Quit"); mi.setMnemonic(KeyEvent.VK_Q); mi.addActionListener(log); file.add(mi); mi = new JMenuItem("Clear"); mi.setMnemonic(KeyEvent.VK_C); mi.addActionListener(log); edit.add(mi); mi = new JMenuItem("Directions"); mi.setMnemonic(KeyEvent.VK_D); mi.addActionListener(log); help.add(mi); mi = new JMenuItem("License"); mi.setMnemonic(KeyEvent.VK_L); mi.addActionListener(log); help.add(mi); help.add(new JSeparator()); mi = new JMenuItem("About"); mi.setMnemonic(KeyEvent.VK_A); mi.addActionListener(log); help.add(mi); f.pack(); f.setVisible(true); } }); } } // // // IntegerDocument // // Allows only digit characters or +- to be entered into the Document. // // package com.knutejohnson.classes; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.text.*; public class IntegerDocument extends PlainDocument { private int length = 0; private boolean sign = false; public IntegerDocument() { } public IntegerDocument(int length) { this.length = length; } public IntegerDocument(int length, boolean sign) { this.length = length; this.sign = sign; } public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if (str == null) { return; } // if sign flag and offset is 0 keep the first char if it is +- if (sign && offs == 0 && str.matches("[+-].*")) { String firstChar = str.substring(0,1); str = str.substring(1).replaceAll("\\D",""); str = firstChar.concat(str); } else str = str.replaceAll("\\D",""); if (length > 0) if (str.length() + getLength() > length) str = str.substring(0,length - getLength()); super.insertString(offs,str,a); } } // // // DoubleDocument // // Allows only those characters allowed in a double to be entered. // // package com.knutejohnson.classes; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.text.*; public class DoubleDocument extends PlainDocument { private int length = 0; private boolean sign = false; public DoubleDocument() { } public DoubleDocument(int length) { this.length = length; } public DoubleDocument(int length, boolean sign) { this.length = length; this.sign = sign; } public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if (str == null) { return; } // if the sign flag is true and the offset is 0, // keep the first char if it is +- if (sign && offs == 0 && str.matches("[+-].*")) { String firstChar = str.substring(0,1); str = str.substring(1).replaceAll("[\\D&&[^\\.]]",""); str = firstChar.concat(str); } else str = str.replaceAll("[\\D&&[^\\.]]",""); if (length > 0) if (str.length() + getLength() > length) str = str.substring(0,length - getLength()); super.insertString(offs,str,a); } } // // // FixedLengthDocument // // package com.knutejohnson.classes; import javax.swing.text.*; public class FixedLengthDocument extends PlainDocument { private volatile int len; public FixedLengthDocument(int len) { this.len = len; } public void setDocumentLength(int len) { this.len = len; } public int getDocumentLength() { return len; } public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if (str == null) return; int strLen = str.length(); int textLen = getLength(); if (strLen + textLen <= len) super.insertString(offs, str, a); } }