From f03ca0ab8799bfa5100eaa2577cfd7b9c37d05bf Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 7 Aug 2011 14:52:29 -0700 Subject: [PATCH] altosui: Add dialogs to configure 'common' frequencies These are stored in preferences, but not yet hooked up to the TM/TD configure dialogs Signed-off-by: Keith Packard --- altosui/AltosConfigFreqUI.java | 418 +++++++++++++++++++++++++++++++++ altosui/AltosConfigureUI.java | 18 +- altosui/AltosFrequency.java | 51 ++++ altosui/AltosPreferences.java | 66 ++++++ altosui/Makefile.am | 2 + 5 files changed, 553 insertions(+), 2 deletions(-) create mode 100644 altosui/AltosConfigFreqUI.java create mode 100644 altosui/AltosFrequency.java diff --git a/altosui/AltosConfigFreqUI.java b/altosui/AltosConfigFreqUI.java new file mode 100644 index 00000000..d68151ec --- /dev/null +++ b/altosui/AltosConfigFreqUI.java @@ -0,0 +1,418 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import javax.swing.event.*; +import javax.swing.plaf.basic.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.*; + +class AltosEditFreqUI extends JDialog implements ActionListener { + Frame frame; + JTextField frequency; + JTextField description; + JButton ok_button, cancel_button; + boolean got_ok; + + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + + if ("ok".equals(cmd)) { + got_ok = true; + setVisible(false); + } + if ("cancel".equals(cmd)) { + got_ok = false; + setVisible(false); + } + } + + public AltosFrequency get() { + if (!got_ok) + return null; + + String f_s = frequency.getText(); + String d_s = description.getText(); + + try { + double f_d = Double.parseDouble(f_s); + + return new AltosFrequency(f_d, d_s); + } catch (NumberFormatException ne) { + } + return null; + } + + public AltosEditFreqUI(Frame in_frame, AltosFrequency existing) { + super(in_frame, true); + + got_ok = false; + frame = in_frame; + + Container pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets (4,4,4,4); + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(new JLabel("Frequency"), c); + + frequency = new JTextField(12); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.gridx = 1; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(frequency, c); + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(new JLabel("Description"), c); + + description = new JTextField(12); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.gridx = 1; + c.gridy = 1; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(description, c); + + ok_button = new JButton("OK"); + ok_button.setActionCommand("ok"); + ok_button.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(ok_button, c); + + cancel_button = new JButton("Cancel"); + cancel_button.setActionCommand("cancel"); + cancel_button.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.gridx = 1; + c.gridy = 2; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(cancel_button, c); + + if (existing == null) + setTitle("Add New Frequency"); + else { + setTitle("Edit Existing Frequency"); + frequency.setText(String.format("%7.3f", existing.frequency)); + description.setText(existing.description); + } + getRootPane().setDefaultButton(ok_button); + + pack(); + setLocationRelativeTo(frame); + + } + + public AltosEditFreqUI(Frame in_frame) { + this(in_frame, (AltosFrequency) null); + } +} + +public class AltosConfigFreqUI extends JDialog implements ActionListener { + + Frame frame; + LinkedList listeners; + + class FrequencyList extends JList { + DefaultListModel list_model; + + public void add(AltosFrequency frequency) { + int i; + for (i = 0; i < list_model.size(); i++) { + AltosFrequency f = (AltosFrequency) list_model.get(i); + if (f.frequency == frequency.frequency) + return; + if (f.frequency > frequency.frequency) + break; + } + list_model.insertElementAt(frequency, i); + } + + public void remove(AltosFrequency frequency) { + list_model.removeElement(frequency); + } + + //Subclass JList to workaround bug 4832765, which can cause the + //scroll pane to not let the user easily scroll up to the beginning + //of the list. An alternative would be to set the unitIncrement + //of the JScrollBar to a fixed value. You wouldn't get the nice + //aligned scrolling, but it should work. + public int getScrollableUnitIncrement(Rectangle visibleRect, + int orientation, + int direction) { + int row; + if (orientation == SwingConstants.VERTICAL && + direction < 0 && (row = getFirstVisibleIndex()) != -1) { + Rectangle r = getCellBounds(row, row); + if ((r.y == visibleRect.y) && (row != 0)) { + Point loc = r.getLocation(); + loc.y--; + int prevIndex = locationToIndex(loc); + Rectangle prevR = getCellBounds(prevIndex, prevIndex); + + if (prevR == null || prevR.y >= r.y) { + return 0; + } + return prevR.height; + } + } + return super.getScrollableUnitIncrement( + visibleRect, orientation, direction); + } + + public AltosFrequency selected() { + AltosFrequency f = (AltosFrequency) getSelectedValue(); + return f; + } + + public AltosFrequency[] frequencies() { + AltosFrequency[] ret; + + ret = new AltosFrequency[list_model.size()]; + for (int i = 0; i < list_model.size(); i++) + ret[i] = (AltosFrequency) list_model.get(i); + return ret; + } + + public FrequencyList(AltosFrequency[] in_frequencies) { + list_model = new DefaultListModel(); + setModel(list_model); + setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + setLayoutOrientation(JList.HORIZONTAL_WRAP); + for (int i = 0; i < in_frequencies.length; i++) { + add(in_frequencies[i]); + } + setVisibleRowCount(in_frequencies.length); + } + } + + FrequencyList frequencies; + + void save_frequencies() { + AltosPreferences.set_common_frequencies(frequencies.frequencies()); + } + + JButton add, edit, remove; + + JButton cancel, ok; + + public void actionPerformed(ActionEvent e) { + String cmd = e.getActionCommand(); + + if ("ok".equals(cmd)) { + save_frequencies(); + setVisible(false); + } else if ("cancel".equals(cmd)) { + setVisible(false); + } else if ("add".equals(cmd)) { + AltosEditFreqUI ui = new AltosEditFreqUI(frame); + ui.setVisible(true); + AltosFrequency f = ui.get(); + if (f != null) + frequencies.add(f); + } else if ("edit".equals(cmd)) { + AltosFrequency old_f = frequencies.selected(); + if (old_f == null) + return; + AltosEditFreqUI ui = new AltosEditFreqUI(frame, old_f); + ui.setVisible(true); + AltosFrequency new_f = ui.get(); + if (new_f != null) { + if (old_f != null) + frequencies.remove(old_f); + frequencies.add(new_f); + } + } else if ("remove".equals(cmd)) { + AltosFrequency old_f = frequencies.selected(); + if (old_f == null) + return; + int ret = JOptionPane.showConfirmDialog(this, + String.format("Remove frequency \"%s\"?", + old_f.toShortString()), + "Remove Frequency", + JOptionPane.YES_NO_OPTION); + if (ret == JOptionPane.YES_OPTION) + frequencies.remove(old_f); + } + } + + public AltosFrequency[] frequencies() { + return frequencies.frequencies(); + } + + public AltosConfigFreqUI(Frame in_frame, + AltosFrequency[] in_frequencies) { + super(in_frame, "Manage Frequencies", true); + + frame = in_frame; + + listeners = new LinkedList(); + + frequencies = new FrequencyList(in_frequencies); + + Container pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(4,4,4,4); + + /* + * Frequencies label and list + */ + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(new JLabel("Frequencies"), c); + + JScrollPane list_scroller = new JScrollPane(frequencies); + list_scroller.setAlignmentX(LEFT_ALIGNMENT); + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.WEST; + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 6; + c.gridheight = 2; + c.weightx = 1; + c.weighty = 1; + pane.add(list_scroller, c); + + add = new JButton("Add"); + add.setActionCommand("add"); + add.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 2; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(add, c); + + edit = new JButton("Edit"); + edit.setActionCommand("edit"); + edit.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 2; + c.gridy = 3; + c.gridwidth = 2; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(edit, c); + + remove = new JButton("Remove"); + remove.setActionCommand("remove"); + remove.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 4; + c.gridy = 3; + c.gridwidth = 2; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(remove, c); + + ok = new JButton("OK"); + ok.setActionCommand("ok"); + ok.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 3; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(ok, c); + + cancel = new JButton("Cancel"); + cancel.setActionCommand("cancel"); + cancel.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 3; + c.gridy = 4; + c.gridwidth = 3; + c.gridheight = 1; + c.weightx = 0; + c.weighty = 0; + pane.add(cancel, c); + + pack(); + setLocationRelativeTo(frame); + } + + public static void show(Component frameComp) { + Frame frame = JOptionPane.getFrameForComponent(frameComp); + AltosConfigFreqUI dialog; + + dialog = new AltosConfigFreqUI(frame, AltosPreferences.common_frequencies()); + dialog.setVisible(true); + } + +} diff --git a/altosui/AltosConfigureUI.java b/altosui/AltosConfigureUI.java index 0f5e4a3b..a8a70ffd 100644 --- a/altosui/AltosConfigureUI.java +++ b/altosui/AltosConfigureUI.java @@ -50,6 +50,7 @@ public class AltosConfigureUI JRadioButton serial_debug; JButton manage_bluetooth; + JButton manage_frequencies; /* DocumentListener interface methods */ public void changedUpdate(DocumentEvent e) { @@ -207,13 +208,26 @@ public class AltosConfigureUI AltosBTManage.show(owner, Altos.bt_known); } }); - c.gridx = 1; + c.gridx = 0; c.gridy = 6; - c.gridwidth = 3; + c.gridwidth = 2; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.WEST; pane.add(manage_bluetooth, c); + manage_frequencies = new JButton("Manage Frequencies"); + manage_frequencies.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + AltosConfigFreqUI.show(owner); + } + }); + c.gridx = 2; + c.gridy = 6; + c.gridwidth = 2; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(manage_frequencies, c); + /* And a close button at the bottom */ close = new JButton("Close"); close.addActionListener(new ActionListener() { diff --git a/altosui/AltosFrequency.java b/altosui/AltosFrequency.java new file mode 100644 index 00000000..8265eafc --- /dev/null +++ b/altosui/AltosFrequency.java @@ -0,0 +1,51 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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; version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import javax.swing.event.*; +import javax.swing.plaf.basic.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.*; + +public class AltosFrequency { + double frequency; + String description; + + public String toString() { + return String.format("%7.3f MHz %-20.20s", + frequency, description); + } + + public String toShortString() { + return String.format("%7.3f MHz %s", + frequency, description); + } + + public AltosFrequency(double f, String d) { + frequency = f; + description = d; + } +} \ No newline at end of file diff --git a/altosui/AltosPreferences.java b/altosui/AltosPreferences.java index c8dee743..e92b9532 100644 --- a/altosui/AltosPreferences.java +++ b/altosui/AltosPreferences.java @@ -64,6 +64,9 @@ class AltosPreferences { /* Channel (map serial to channel) */ static Hashtable channels; + /* Frequency (map serial to frequency) */ + static Hashtable frequencies; + /* Telemetry (map serial to telemetry format) */ static Hashtable telemetries; @@ -79,6 +82,55 @@ class AltosPreferences { /* Serial debug */ static boolean serial_debug; + /* List of frequencies */ + final static String common_frequencies_node_name = "COMMON-FREQUENCIES"; + static AltosFrequency[] common_frequencies; + + final static String frequency_count = "COUNT"; + final static String frequency_format = "FREQUENCY-%d"; + final static String description_format = "DESCRIPTION-%d"; + + static AltosFrequency[] load_common_frequencies() { + AltosFrequency[] frequencies = null; + boolean existing = false; + try { + existing = preferences.nodeExists(common_frequencies_node_name); + } catch (BackingStoreException be) { + existing = false; + } + if (existing) { + Preferences node = preferences.node(common_frequencies_node_name); + int count = node.getInt(frequency_count, 0); + + frequencies = new AltosFrequency[count]; + for (int i = 0; i < count; i++) { + double frequency; + String description; + + frequency = node.getDouble(String.format(frequency_format, i), 0.0); + description = node.get(String.format(description_format, i), null); + frequencies[i] = new AltosFrequency(frequency, description); + } + } else { + frequencies = new AltosFrequency[10]; + for (int i = 0; i < 10; i++) { + frequencies[i] = new AltosFrequency(434.550 + i * .1, + String.format("Channel %d", i)); + } + } + return frequencies; + } + + static void save_common_frequencies(AltosFrequency[] frequencies) { + Preferences node = preferences.node(common_frequencies_node_name); + + node.putInt(frequency_count, frequencies.length); + for (int i = 0; i < frequencies.length; i++) { + node.putDouble(String.format(frequency_format, i), frequencies[i].frequency); + node.put(String.format(description_format, i), frequencies[i].description); + } + } + public static void init() { preferences = Preferences.userRoot().node("/org/altusmetrum/altosui"); @@ -112,6 +164,8 @@ class AltosPreferences { serial_debug = preferences.getBoolean(serialDebugPreference, false); AltosSerial.set_debug(serial_debug); + + common_frequencies = load_common_frequencies(); } static { init(); } @@ -273,4 +327,16 @@ class AltosPreferences { public static Preferences bt_devices() { return preferences.node("bt_devices"); } + + public static AltosFrequency[] common_frequencies() { + return common_frequencies; + } + + public static void set_common_frequencies(AltosFrequency[] frequencies) { + common_frequencies = frequencies; + synchronized(preferences) { + save_common_frequencies(frequencies); + flush_preferences(); + } + } } diff --git a/altosui/Makefile.am b/altosui/Makefile.am index 008bd097..d6fd0e6d 100644 --- a/altosui/Makefile.am +++ b/altosui/Makefile.am @@ -16,6 +16,7 @@ altosui_JAVA = \ AltosChannelMenu.java \ AltosConfig.java \ AltosConfigData.java \ + AltosConfigFreqUI.java \ AltosConfigUI.java \ AltosConfigureUI.java \ AltosConvert.java \ @@ -50,6 +51,7 @@ altosui_JAVA = \ AltosFlightReader.java \ AltosFlightStatus.java \ AltosFlightUI.java \ + AltosFrequency.java \ AltosGPS.java \ AltosGPSSat.java \ AltosGreatCircle.java \ -- 2.30.2