summaryrefslogtreecommitdiff
path: root/altosuilib
diff options
context:
space:
mode:
Diffstat (limited to 'altosuilib')
-rw-r--r--altosuilib/AltosBTDevice.java125
-rw-r--r--altosuilib/AltosBTDeviceIterator.java64
-rw-r--r--altosuilib/AltosBTKnown.java101
-rw-r--r--altosuilib/AltosBTManage.java353
-rw-r--r--altosuilib/AltosCSVUI.java103
-rw-r--r--altosuilib/AltosConfigFreqUI.java411
-rw-r--r--altosuilib/AltosDataChooser.java78
-rw-r--r--altosuilib/AltosDevice.java2
-rw-r--r--altosuilib/AltosDeviceDialog.java20
-rw-r--r--altosuilib/AltosDeviceUIDialog.java69
-rw-r--r--altosuilib/AltosDisplayThread.java263
-rw-r--r--altosuilib/AltosEepromDelete.java143
-rw-r--r--altosuilib/AltosEepromManage.java241
-rw-r--r--altosuilib/AltosEepromMonitor.java251
-rw-r--r--altosuilib/AltosEepromMonitorUI.java310
-rw-r--r--altosuilib/AltosEepromSelect.java183
-rw-r--r--altosuilib/AltosFlashUI.java438
-rw-r--r--altosuilib/AltosFlightDisplay.java28
-rw-r--r--altosuilib/AltosFlightInfoTableModel.java75
-rw-r--r--altosuilib/AltosFlightStatsTable.java167
-rw-r--r--altosuilib/AltosFontListener.java2
-rw-r--r--altosuilib/AltosFreqList.java85
-rw-r--r--altosuilib/AltosGraph.java445
-rw-r--r--altosuilib/AltosGraphDataPoint.java249
-rw-r--r--altosuilib/AltosGraphDataSet.java94
-rw-r--r--altosuilib/AltosInfoTable.java264
-rw-r--r--altosuilib/AltosLed.java45
-rw-r--r--altosuilib/AltosLights.java65
-rw-r--r--altosuilib/AltosPositionListener.java2
-rw-r--r--altosuilib/AltosRomconfigUI.java190
-rw-r--r--altosuilib/AltosScanUI.java569
-rw-r--r--altosuilib/AltosSerial.java208
-rw-r--r--altosuilib/AltosSerialInUseException.java26
-rw-r--r--altosuilib/AltosUIAxis.java6
-rw-r--r--altosuilib/AltosUIConfigure.java31
-rw-r--r--altosuilib/AltosUIDataMissing.java2
-rw-r--r--altosuilib/AltosUIDataPoint.java2
-rw-r--r--altosuilib/AltosUIDataSet.java2
-rw-r--r--altosuilib/AltosUIDialog.java2
-rw-r--r--altosuilib/AltosUIEnable.java14
-rw-r--r--altosuilib/AltosUIFlightTab.java88
-rw-r--r--altosuilib/AltosUIFrame.java15
-rw-r--r--altosuilib/AltosUIGraph.java12
-rw-r--r--altosuilib/AltosUIGrapher.java6
-rw-r--r--altosuilib/AltosUIIndicator.java181
-rw-r--r--altosuilib/AltosUILatLon.java44
-rw-r--r--altosuilib/AltosUILib.java4
-rw-r--r--altosuilib/AltosUIListener.java2
-rw-r--r--altosuilib/AltosUIMap.java250
-rw-r--r--altosuilib/AltosUIMapCache.java114
-rw-r--r--altosuilib/AltosUIMapImage.java113
-rw-r--r--altosuilib/AltosUIMapLine.java116
-rw-r--r--altosuilib/AltosUIMapMark.java59
-rw-r--r--altosuilib/AltosUIMapPath.java96
-rw-r--r--altosuilib/AltosUIMapPreload.java607
-rw-r--r--altosuilib/AltosUIMapRectangle.java45
-rw-r--r--altosuilib/AltosUIMapStore.java203
-rw-r--r--altosuilib/AltosUIMapStoreListener.java22
-rw-r--r--altosuilib/AltosUIMapTile.java192
-rw-r--r--altosuilib/AltosUIMapTileListener.java24
-rw-r--r--altosuilib/AltosUIMapTransform.java106
-rw-r--r--altosuilib/AltosUIMapView.java472
-rw-r--r--altosuilib/AltosUIMapZoomListener.java22
-rw-r--r--altosuilib/AltosUIMarker.java6
-rw-r--r--altosuilib/AltosUIPreferences.java4
-rw-r--r--altosuilib/AltosUIPreferencesBackend.java6
-rw-r--r--altosuilib/AltosUISeries.java9
-rw-r--r--altosuilib/AltosUIUnitsIndicator.java122
-rw-r--r--altosuilib/AltosUIVersion.java.in8
-rw-r--r--altosuilib/AltosUIVoltageIndicator.java42
-rw-r--r--altosuilib/AltosUSBDevice.java2
-rw-r--r--altosuilib/AltosVoice.java94
-rw-r--r--altosuilib/GrabNDrag.java55
-rw-r--r--altosuilib/Makefile.am70
74 files changed, 8862 insertions, 77 deletions
diff --git a/altosuilib/AltosBTDevice.java b/altosuilib/AltosBTDevice.java
new file mode 100644
index 00000000..beefa532
--- /dev/null
+++ b/altosuilib/AltosBTDevice.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import libaltosJNI.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosBTDevice extends altos_bt_device implements AltosDevice {
+
+ public String getProductName() {
+ String name = getName();
+ if (name == null)
+ return "Altus Metrum";
+ int dash = name.lastIndexOf("-");
+ if (dash < 0)
+ return name;
+ return name.substring(0,dash);
+ }
+
+ public int getProduct() {
+ if (AltosLib.bt_product_telebt.equals(getProductName()))
+ return AltosLib.product_telebt;
+ return 0;
+ }
+
+ public String getPath() {
+ return getAddr();
+ }
+
+ public String getErrorString() {
+ altos_error error = new altos_error();
+
+ libaltos.altos_get_last_error(error);
+ return String.format("%s (%d)", error.getString(), error.getCode());
+ }
+
+ public int getSerial() {
+ String name = getName();
+ if (name == null)
+ return 0;
+ int dash = name.lastIndexOf("-");
+ if (dash < 0 || dash >= name.length())
+ return 0;
+ String sn = name.substring(dash + 1, name.length());
+ try {
+ return Integer.parseInt(sn);
+ } catch (NumberFormatException ne) {
+ return 0;
+ }
+ }
+
+ public String toString() {
+ return String.format("%-20.20s %4d %s",
+ getProductName(), getSerial(), getAddr());
+ }
+
+ public String toShortString() {
+ return String.format("%s %d %s",
+ getProductName(), getSerial(), getAddr());
+
+ }
+
+ public SWIGTYPE_p_altos_file open() {
+ return libaltos.altos_bt_open(this);
+ }
+
+ /*
+ private boolean isAltusMetrum() {
+ if (getName().startsWith(Altos.bt_product_telebt))
+ return true;
+ return false;
+ }
+ */
+
+ public boolean matchProduct(int want_product) {
+
+// if (!isAltusMetrum())
+// return false;
+
+ if (want_product == AltosLib.product_any)
+ return true;
+
+ if (want_product == AltosLib.product_basestation)
+ return matchProduct(AltosLib.product_telebt);
+
+ if (want_product == getProduct())
+ return true;
+
+ return false;
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof AltosBTDevice))
+ return false;
+ AltosBTDevice other = (AltosBTDevice) o;
+ return getName().equals(other.getName()) && getAddr().equals(other.getAddr());
+ }
+
+ public int hashCode() {
+ return getName().hashCode() ^ getAddr().hashCode();
+ }
+
+ public AltosBTDevice(String name, String addr) {
+ AltosUILib.load_library();
+ libaltos.altos_bt_fill_in(name, addr,this);
+ }
+
+ public AltosBTDevice() {
+ }
+}
diff --git a/altosuilib/AltosBTDeviceIterator.java b/altosuilib/AltosBTDeviceIterator.java
new file mode 100644
index 00000000..cad60ffb
--- /dev/null
+++ b/altosuilib/AltosBTDeviceIterator.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.util.*;
+import libaltosJNI.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosBTDeviceIterator implements Iterator<AltosBTDevice> {
+ AltosBTDevice current;
+ boolean done;
+ SWIGTYPE_p_altos_bt_list list;
+
+ public boolean hasNext() {
+ if (list == null)
+ return false;
+ if (current != null)
+ return true;
+ if (done)
+ return false;
+ current = new AltosBTDevice();
+ while (libaltos.altos_bt_list_next(list, current) != 0) {
+// if (current.matchProduct(product))
+ return true;
+ }
+ current = null;
+ done = true;
+ return false;
+ }
+
+ public AltosBTDevice next() {
+ if (hasNext()) {
+ AltosBTDevice next = current;
+ current = null;
+ return next;
+ }
+ return null;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public AltosBTDeviceIterator(int inquiry_time) {
+ done = false;
+ current = null;
+ list = libaltos.altos_bt_list_start(inquiry_time);
+ }
+}
diff --git a/altosuilib/AltosBTKnown.java b/altosuilib/AltosBTKnown.java
new file mode 100644
index 00000000..02883c75
--- /dev/null
+++ b/altosuilib/AltosBTKnown.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.util.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosBTKnown implements Iterable<AltosBTDevice> {
+ LinkedList<AltosBTDevice> devices = new LinkedList<AltosBTDevice>();
+ AltosPreferencesBackend bt_pref = AltosUIPreferences.bt_devices();
+
+ private String get_address(String name) {
+ return bt_pref.getString(name, "");
+ }
+
+ private void set_address(String name, String addr) {
+ bt_pref.putString(name, addr);
+ }
+
+ private void remove(String name) {
+ bt_pref.remove(name);
+ }
+
+ private void load() {
+ try {
+ String[] names = bt_pref.keys();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ String addr = get_address(name);
+ devices.add(new AltosBTDevice(name, addr));
+ }
+ } catch (IllegalStateException ie) {
+ }
+ }
+
+ public Iterator<AltosBTDevice> iterator() {
+ return devices.iterator();
+ }
+
+ private void flush() {
+ AltosUIPreferences.flush_preferences();
+ }
+
+ public void set(Iterable<AltosBTDevice> new_devices) {
+ for (AltosBTDevice old : devices) {
+ boolean found = false;
+ for (AltosBTDevice new_device : new_devices) {
+ if (new_device.equals(old)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ remove(old.getName());
+ }
+ devices = new LinkedList<AltosBTDevice>();
+ for (AltosBTDevice new_device : new_devices) {
+ devices.add(new_device);
+ set_address(new_device.getName(), new_device.getAddr());
+ }
+ flush();
+ }
+
+ public List<AltosDevice> list(int product) {
+ LinkedList<AltosDevice> list = new LinkedList<AltosDevice>();
+ for (AltosBTDevice device : devices) {
+ if (device.matchProduct(product))
+ list.add(device);
+ }
+ return list;
+ }
+
+ public AltosBTKnown() {
+ devices = new LinkedList<AltosBTDevice>();
+ bt_pref = AltosUIPreferences.bt_devices();
+ load();
+ }
+
+ static AltosBTKnown known;
+
+ static public AltosBTKnown bt_known() {
+ if (known == null)
+ known = new AltosBTKnown();
+ return known;
+ }
+}
diff --git a/altosuilib/AltosBTManage.java b/altosuilib/AltosBTManage.java
new file mode 100644
index 00000000..6da0a3eb
--- /dev/null
+++ b/altosuilib/AltosBTManage.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.plaf.basic.*;
+import java.util.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosBTManage extends AltosUIDialog implements ActionListener, Iterable<AltosBTDevice> {
+ LinkedBlockingQueue<AltosBTDevice> found_devices;
+ Frame frame;
+ LinkedList<ActionListener> listeners;
+ AltosBTKnown bt_known;
+
+ class DeviceList extends JList<AltosBTDevice> implements Iterable<AltosBTDevice> {
+ LinkedList<AltosBTDevice> devices;
+ DefaultListModel<AltosBTDevice> list_model;
+
+ public void add (AltosBTDevice device) {
+ if (!devices.contains(device)) {
+ devices.add(device);
+ list_model.addElement(device);
+ }
+ }
+
+ public void remove (AltosBTDevice device) {
+ if (devices.contains(device)) {
+ devices.remove(device);
+ list_model.removeElement(device);
+ }
+ }
+
+ public boolean contains(AltosBTDevice device) {
+ return devices.contains(device);
+ }
+
+ //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 Iterator<AltosBTDevice> iterator() {
+ return devices.iterator();
+ }
+
+ public java.util.List<AltosBTDevice> selected_list() throws InterruptedException {
+ return getSelectedValuesList();
+ }
+
+ public DeviceList() {
+ devices = new LinkedList<AltosBTDevice>();
+ list_model = new DefaultListModel<AltosBTDevice>();
+ setModel(list_model);
+ setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ setLayoutOrientation(JList.HORIZONTAL_WRAP);
+ setVisibleRowCount(-1);
+ }
+ }
+
+ DeviceList visible_devices;
+
+ DeviceList known_devices;
+ Thread bt_thread;
+
+ public Iterator<AltosBTDevice> iterator() {
+ return known_devices.iterator();
+ }
+
+ public void commit() {
+ bt_known.set(this);
+ }
+
+ public void add_known() {
+ try {
+ for (AltosBTDevice device : visible_devices.selected_list()) {
+ known_devices.add(device);
+ visible_devices.remove(device);
+ }
+ } catch (InterruptedException ie) {
+ }
+ }
+
+ public void remove_known() {
+ try {
+ for (AltosBTDevice device : known_devices.selected_list()) {
+ known_devices.remove(device);
+ visible_devices.add(device);
+ }
+ } catch (InterruptedException ie) {
+ }
+ }
+
+ public void addActionListener(ActionListener l) {
+ listeners.add(l);
+ }
+
+ private void forwardAction(ActionEvent e) {
+ for (ActionListener l : listeners)
+ l.actionPerformed(e);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ String command = e.getActionCommand();
+ if ("ok".equals(command)) {
+ bt_thread.interrupt();
+ commit();
+ setVisible(false);
+ forwardAction(e);
+ } else if ("cancel".equals(command)) {
+ bt_thread.interrupt();
+ setVisible(false);
+ forwardAction(e);
+ } else if ("select".equals(command)) {
+ add_known();
+ } else if ("deselect".equals(command)) {
+ remove_known();
+ }
+ }
+
+ public void got_visible_device() {
+ while (!found_devices.isEmpty()) {
+ AltosBTDevice device = found_devices.remove();
+ if (!known_devices.contains(device))
+ visible_devices.add(device);
+ }
+ }
+
+ class BTGetVisibleDevices implements Runnable {
+ public void run () {
+ for (;;)
+ for (int time = 1; time <= 8; time <<= 1) {
+ AltosBTDeviceIterator i = new AltosBTDeviceIterator(time);
+ AltosBTDevice device;
+
+ if (Thread.interrupted())
+ return;
+ try {
+ while ((device = i.next()) != null) {
+ Runnable r;
+
+ if (Thread.interrupted())
+ return;
+ found_devices.add(device);
+ r = new Runnable() {
+ public void run() {
+ got_visible_device();
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+ } catch (Exception e) {
+ System.out.printf("uh-oh, exception %s\n", e.toString());
+ }
+ }
+ }
+ }
+
+ public static void show(Component frameComp, AltosBTKnown known) {
+ Frame frame = JOptionPane.getFrameForComponent(frameComp);
+ AltosBTManage dialog;
+
+ dialog = new AltosBTManage(frame, known);
+ dialog.setVisible(true);
+ }
+
+ public AltosBTManage(Frame in_frame, AltosBTKnown in_known) {
+ super(in_frame, "Manage Bluetooth Devices", true);
+
+ frame = in_frame;
+ bt_known = in_known;
+ BTGetVisibleDevices get_visible_devices = new BTGetVisibleDevices();
+ bt_thread = new Thread(get_visible_devices);
+ bt_thread.start();
+
+ listeners = new LinkedList<ActionListener>();
+
+ found_devices = new LinkedBlockingQueue<AltosBTDevice>();
+
+ Container pane = getContentPane();
+ pane.setLayout(new GridBagLayout());
+
+ GridBagConstraints c = new GridBagConstraints();
+ c.insets = new Insets(4,4,4,4);
+
+ /*
+ * Known devices 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("Known Devices"), c);
+
+ known_devices = new DeviceList();
+ for (AltosBTDevice device : bt_known)
+ known_devices.add(device);
+
+ JScrollPane known_list_scroller = new JScrollPane(known_devices);
+ known_list_scroller.setPreferredSize(new Dimension(400, 80));
+ known_list_scroller.setAlignmentX(LEFT_ALIGNMENT);
+ c.fill = GridBagConstraints.BOTH;
+ c.anchor = GridBagConstraints.WEST;
+ c.gridx = 0;
+ c.gridy = 1;
+ c.gridwidth = 1;
+ c.gridheight = 2;
+ c.weightx = 1;
+ c.weighty = 1;
+ pane.add(known_list_scroller, c);
+
+ /*
+ * Visible devices label and list
+ */
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.WEST;
+ c.gridx = 2;
+ c.gridy = 0;
+ c.gridwidth = 1;
+ c.gridheight = 1;
+ c.weightx = 0;
+ c.weighty = 0;
+
+ pane.add(new JLabel("Visible Devices"), c);
+
+ visible_devices = new DeviceList();
+ JScrollPane visible_list_scroller = new JScrollPane(visible_devices);
+ visible_list_scroller.setPreferredSize(new Dimension(400, 80));
+ visible_list_scroller.setAlignmentX(LEFT_ALIGNMENT);
+ c.fill = GridBagConstraints.BOTH;
+ c.anchor = GridBagConstraints.WEST;
+ c.gridx = 2;
+ c.gridy = 1;
+ c.gridheight = 2;
+ c.gridwidth = 1;
+ c.weightx = 1;
+ c.weighty = 1;
+ pane.add(visible_list_scroller, c);
+
+ /*
+ * Arrows between the two lists
+ */
+ BasicArrowButton select_arrow = new BasicArrowButton(SwingConstants.WEST);
+ select_arrow.setActionCommand("select");
+ select_arrow.addActionListener(this);
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.SOUTH;
+ c.gridx = 1;
+ c.gridy = 1;
+ c.gridheight = 1;
+ c.gridwidth = 1;
+ c.weightx = 0;
+ c.weighty = 0;
+ pane.add(select_arrow, c);
+
+ BasicArrowButton deselect_arrow = new BasicArrowButton(SwingConstants.EAST);
+ deselect_arrow.setActionCommand("deselect");
+ deselect_arrow.addActionListener(this);
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.NORTH;
+ c.gridx = 1;
+ c.gridy = 2;
+ c.gridheight = 1;
+ c.gridwidth = 1;
+ c.weightx = 0;
+ c.weighty = 0;
+ pane.add(deselect_arrow, c);
+
+ JButton cancel_button = new JButton("Cancel");
+ cancel_button.setActionCommand("cancel");
+ cancel_button.addActionListener(this);
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.gridx = 0;
+ c.gridy = 3;
+ c.gridheight = 1;
+ c.gridwidth = 1;
+ c.weightx = 0;
+ c.weighty = 0;
+ pane.add(cancel_button, c);
+
+ JButton ok_button = new JButton("OK");
+ ok_button.setActionCommand("ok");
+ ok_button.addActionListener(this);
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.gridx = 2;
+ c.gridy = 3;
+ c.gridheight = 1;
+ c.gridwidth = 1;
+ c.weightx = 0;
+ c.weighty = 0;
+ pane.add(ok_button, c);
+
+ getRootPane().setDefaultButton(ok_button);
+
+ pack();
+ setLocationRelativeTo(frame);
+ setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent e) {
+ bt_thread.interrupt();
+ setVisible(false);
+ }
+ });
+ }
+}
diff --git a/altosuilib/AltosCSVUI.java b/altosuilib/AltosCSVUI.java
new file mode 100644
index 00000000..0a5e4fa2
--- /dev/null
+++ b/altosuilib/AltosCSVUI.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosCSVUI
+ extends AltosUIDialog
+ implements ActionListener
+{
+ JFileChooser csv_chooser;
+ JPanel accessory;
+ JComboBox<String> combo_box;
+ Iterable<AltosState> states;
+ AltosWriter writer;
+
+ static String[] combo_box_items = { "Comma Separated Values (.CSV)", "Googleearth Data (.KML)" };
+
+ void set_default_file() {
+ File current = csv_chooser.getSelectedFile();
+ String current_name = current.getName();
+ String new_name = null;
+ String selected = (String) combo_box.getSelectedItem();
+
+ if (selected.contains("CSV"))
+ new_name = AltosLib.replace_extension(current_name, ".csv");
+ else if (selected.contains("KML"))
+ new_name = AltosLib.replace_extension(current_name, ".kml");
+ if (new_name != null)
+ csv_chooser.setSelectedFile(new File(new_name));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().equals("comboBoxChanged"))
+ set_default_file();
+ }
+
+ public AltosCSVUI(JFrame frame, AltosStateIterable states, File source_file) {
+ this.states = states;
+ csv_chooser = new JFileChooser(source_file);
+
+ accessory = new JPanel();
+ accessory.setLayout(new GridBagLayout());
+
+ GridBagConstraints c = new GridBagConstraints();
+ c.fill = GridBagConstraints.NONE;
+ c.weightx = 1;
+ c.weighty = 0;
+ c.insets = new Insets (4, 4, 4, 4);
+
+ JLabel accessory_label = new JLabel("Export File Type");
+ c.gridx = 0;
+ c.gridy = 0;
+ accessory.add(accessory_label, c);
+
+ combo_box = new JComboBox<String>(combo_box_items);
+ combo_box.addActionListener(this);
+ c.gridx = 0;
+ c.gridy = 1;
+ accessory.add(combo_box, c);
+
+ csv_chooser.setAccessory(accessory);
+ csv_chooser.setSelectedFile(source_file);
+ set_default_file();
+ int ret = csv_chooser.showSaveDialog(frame);
+ if (ret == JFileChooser.APPROVE_OPTION) {
+ File file = csv_chooser.getSelectedFile();
+ String type = (String) combo_box.getSelectedItem();
+ try {
+ if (type.contains("CSV"))
+ writer = new AltosCSV(file);
+ else
+ writer = new AltosKML(file);
+ writer.write(states);
+ writer.close();
+ } catch (FileNotFoundException ee) {
+ JOptionPane.showMessageDialog(frame,
+ ee.getMessage(),
+ "Cannot open file",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+}
diff --git a/altosuilib/AltosConfigFreqUI.java b/altosuilib/AltosConfigFreqUI.java
new file mode 100644
index 00000000..6dcd63b8
--- /dev/null
+++ b/altosuilib/AltosConfigFreqUI.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.util.*;
+import org.altusmetrum.altoslib_4.*;
+
+class AltosEditFreqUI extends AltosUIDialog 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 AltosUIDialog implements ActionListener {
+
+ Frame frame;
+ LinkedList<ActionListener> listeners;
+
+ class FrequencyList extends JList<AltosFrequency> {
+ DefaultListModel<AltosFrequency> 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 (frequency.frequency == f.frequency)
+ return;
+ if (frequency.frequency < f.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<AltosFrequency>();
+ 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() {
+ AltosUIPreferences.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<ActionListener>();
+
+ 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, AltosUIPreferences.common_frequencies());
+ dialog.setVisible(true);
+ }
+
+}
diff --git a/altosuilib/AltosDataChooser.java b/altosuilib/AltosDataChooser.java
new file mode 100644
index 00000000..59891c4a
--- /dev/null
+++ b/altosuilib/AltosDataChooser.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import java.io.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosDataChooser extends JFileChooser {
+ JFrame frame;
+ String filename;
+ File file;
+
+ public String filename() {
+ return filename;
+ }
+
+ public File file() {
+ return file;
+ }
+
+ public AltosStateIterable runDialog() {
+ int ret;
+
+ ret = showOpenDialog(frame);
+ if (ret == APPROVE_OPTION) {
+ file = getSelectedFile();
+ if (file == null)
+ return null;
+ filename = file.getName();
+ try {
+ if (filename.endsWith("eeprom")) {
+ FileInputStream in = new FileInputStream(file);
+ return new AltosEepromFile(in);
+ } else if (filename.endsWith("telem")) {
+ FileInputStream in = new FileInputStream(file);
+ return new AltosTelemetryFile(in);
+ } else {
+ throw new FileNotFoundException();
+ }
+ } catch (FileNotFoundException fe) {
+ JOptionPane.showMessageDialog(frame,
+ fe.getMessage(),
+ "Cannot open file",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ return null;
+ }
+
+ public AltosDataChooser(JFrame in_frame) {
+ frame = in_frame;
+ setDialogTitle("Select Flight Record File");
+ setFileFilter(new FileNameExtensionFilter("On-board Log file",
+ "eeprom"));
+ setFileFilter(new FileNameExtensionFilter("Telemetry file",
+ "telem"));
+ setFileFilter(new FileNameExtensionFilter("Flight data file",
+ "telem", "eeprom"));
+ setCurrentDirectory(AltosUIPreferences.logdir());
+ }
+}
diff --git a/altosuilib/AltosDevice.java b/altosuilib/AltosDevice.java
index 2461df1b..251ae994 100644
--- a/altosuilib/AltosDevice.java
+++ b/altosuilib/AltosDevice.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import libaltosJNI.*;
diff --git a/altosuilib/AltosDeviceDialog.java b/altosuilib/AltosDeviceDialog.java
index 73bc0b2f..0bedea97 100644
--- a/altosuilib/AltosDeviceDialog.java
+++ b/altosuilib/AltosDeviceDialog.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import javax.swing.*;
import java.awt.*;
@@ -23,14 +23,14 @@ import java.awt.event.*;
public abstract class AltosDeviceDialog extends AltosUIDialog implements ActionListener {
- private AltosDevice value;
- private JList list;
- private JButton cancel_button;
- private JButton select_button;
- public Frame frame;
- public int product;
- public JPanel buttonPane;
-
+ private AltosDevice value;
+ private JList<AltosDevice> list;
+ private JButton cancel_button;
+ private JButton select_button;
+ public Frame frame;
+ public int product;
+ public JPanel buttonPane;
+
public AltosDevice getValue() {
return value;
}
@@ -65,7 +65,7 @@ public abstract class AltosDeviceDialog extends AltosUIDialog implements ActionL
select_button.setEnabled(false);
getRootPane().setDefaultButton(select_button);
- list = new JList(devices) {
+ list = new JList<AltosDevice>(devices) {
//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
diff --git a/altosuilib/AltosDeviceUIDialog.java b/altosuilib/AltosDeviceUIDialog.java
new file mode 100644
index 00000000..3013612a
--- /dev/null
+++ b/altosuilib/AltosDeviceUIDialog.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+
+public class AltosDeviceUIDialog extends AltosDeviceDialog {
+
+ public AltosDevice[] devices() {
+ java.util.List<AltosDevice> usb_devices = AltosUSBDevice.list(product);
+ int num_devices = usb_devices.size();
+ java.util.List<AltosDevice> bt_devices = AltosBTKnown.bt_known().list(product);
+ num_devices += bt_devices.size();
+ AltosDevice[] devices = new AltosDevice[num_devices];
+
+ for (int i = 0; i < usb_devices.size(); i++)
+ devices[i] = usb_devices.get(i);
+ int off = usb_devices.size();
+ for (int j = 0; j < bt_devices.size(); j++)
+ devices[off + j] = bt_devices.get(j);
+ return devices;
+ }
+
+ public void add_bluetooth() {
+ JButton manage_bluetooth_button = new JButton("Manage Bluetooth");
+ manage_bluetooth_button.setActionCommand("manage");
+ manage_bluetooth_button.addActionListener(this);
+ buttonPane.add(manage_bluetooth_button);
+ buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ super.actionPerformed(e);
+ if ("manage".equals(e.getActionCommand())) {
+ AltosBTManage.show(frame, AltosBTKnown.bt_known());
+ update_devices();
+ }
+ }
+
+ public AltosDeviceUIDialog (Frame in_frame, Component location, int in_product) {
+ super(in_frame, location, in_product);
+ }
+
+ public static AltosDevice show (Component frameComp, int product) {
+ Frame frame = JOptionPane.getFrameForComponent(frameComp);
+ AltosDeviceUIDialog dialog;
+
+ dialog = new AltosDeviceUIDialog(frame, frameComp, product);
+ dialog.setVisible(true);
+ return dialog.getValue();
+ }
+}
diff --git a/altosuilib/AltosDisplayThread.java b/altosuilib/AltosDisplayThread.java
new file mode 100644
index 00000000..06bc68a9
--- /dev/null
+++ b/altosuilib/AltosDisplayThread.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import javax.swing.*;
+import java.io.*;
+import java.text.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosDisplayThread extends Thread {
+
+ Frame parent;
+ IdleThread idle_thread;
+ AltosVoice voice;
+ AltosFlightReader reader;
+ AltosState old_state, state;
+ AltosListenerState listener_state;
+ AltosFlightDisplay display;
+
+ synchronized void show_safely() {
+ final AltosState my_state = state;
+ final AltosListenerState my_listener_state = listener_state;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ display.show(my_state, my_listener_state);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ void reading_error_internal() {
+ JOptionPane.showMessageDialog(parent,
+ String.format("Error reading from \"%s\"", reader.name),
+ "Telemetry Read Error",
+ JOptionPane.ERROR_MESSAGE);
+ }
+
+ void reading_error_safely() {
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ reading_error_internal();
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ class IdleThread extends Thread {
+
+ boolean started;
+ int reported_landing;
+ int report_interval;
+ long report_time;
+
+ public synchronized void report(boolean last) {
+ if (state == null)
+ return;
+
+ /* reset the landing count once we hear about a new flight */
+ if (state.state < AltosLib.ao_flight_drogue)
+ reported_landing = 0;
+
+ /* Shut up once the rocket is on the ground */
+ if (reported_landing > 2) {
+ return;
+ }
+
+ /* If the rocket isn't on the pad, then report height */
+ if (AltosLib.ao_flight_drogue <= state.state &&
+ state.state < AltosLib.ao_flight_landed &&
+ state.from_pad != null &&
+ state.range >= 0)
+ {
+ voice.speak("Height %s, bearing %s %d, elevation %d, range %s.\n",
+ AltosConvert.height.say(state.height()),
+ state.from_pad.bearing_words(
+ AltosGreatCircle.BEARING_VOICE),
+ (int) (state.from_pad.bearing + 0.5),
+ (int) (state.elevation + 0.5),
+ AltosConvert.distance.say(state.range));
+ } else if (state.state > AltosLib.ao_flight_pad && state.height() != AltosLib.MISSING) {
+ voice.speak(AltosConvert.height.say_units(state.height()));
+ } else {
+ reported_landing = 0;
+ }
+
+ /* If the rocket is coming down, check to see if it has landed;
+ * either we've got a landed report or we haven't heard from it in
+ * a long time
+ */
+ if (state.state != AltosLib.ao_flight_stateless &&
+ state.state >= AltosLib.ao_flight_drogue &&
+ (last ||
+ System.currentTimeMillis() - state.received_time >= 15000 ||
+ state.state == AltosLib.ao_flight_landed))
+ {
+ if (Math.abs(state.speed()) < 20 && state.height() < 100)
+ voice.speak("rocket landed safely");
+ else
+ voice.speak("rocket may have crashed");
+ if (state.from_pad != null)
+ voice.speak("Bearing %d degrees, range %s.",
+ (int) (state.from_pad.bearing + 0.5),
+ AltosConvert.distance.say_units(state.from_pad.distance));
+ ++reported_landing;
+ if (state.state != AltosLib.ao_flight_landed) {
+ state.state = AltosLib.ao_flight_landed;
+ show_safely();
+ }
+ }
+ }
+
+ long now () {
+ return System.currentTimeMillis();
+ }
+
+ void set_report_time() {
+ report_time = now() + report_interval;
+ }
+
+ public void run () {
+ try {
+ for (;;) {
+ if (reader.has_monitor_battery()) {
+ listener_state.battery = reader.monitor_battery();
+ show_safely();
+ }
+ set_report_time();
+ for (;;) {
+ voice.drain();
+ synchronized (this) {
+ long sleep_time = report_time - now();
+ if (sleep_time <= 0)
+ break;
+ wait(sleep_time);
+ }
+ }
+
+ report(false);
+ }
+ } catch (InterruptedException ie) {
+ try {
+ voice.drain();
+ } catch (InterruptedException iie) { }
+ }
+ }
+
+ public synchronized void notice(boolean spoken) {
+ if (old_state != null && old_state.state != state.state) {
+ report_time = now();
+ this.notify();
+ } else if (spoken)
+ set_report_time();
+ }
+
+ public IdleThread() {
+ reported_landing = 0;
+ report_interval = 10000;
+ }
+ }
+
+ synchronized boolean tell() {
+ boolean ret = false;
+ if (old_state == null || old_state.state != state.state) {
+ if (state.state != AltosLib.ao_flight_stateless)
+ voice.speak(state.state_name());
+ if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&
+ state.state > AltosLib.ao_flight_boost) {
+ if (state.max_speed() != AltosLib.MISSING)
+ voice.speak("max speed: %s.",
+ AltosConvert.speed.say_units(state.max_speed() + 0.5));
+ ret = true;
+ } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&
+ state.state >= AltosLib.ao_flight_drogue) {
+ if (state.max_height() != AltosLib.MISSING)
+ voice.speak("max height: %s.",
+ AltosConvert.height.say_units(state.max_height() + 0.5));
+ ret = true;
+ }
+ }
+ if (old_state == null || old_state.gps_ready != state.gps_ready) {
+ if (state.gps_ready) {
+ voice.speak("GPS ready");
+ ret = true;
+ }
+ else if (old_state != null) {
+ voice.speak("GPS lost");
+ ret = true;
+ }
+ }
+ old_state = state;
+ return ret;
+ }
+
+ public void run() {
+ boolean interrupted = false;
+ boolean told;
+
+ idle_thread = new IdleThread();
+ idle_thread.start();
+
+ try {
+ for (;;) {
+ try {
+ state = reader.read();
+ if (state == null)
+ break;
+ reader.update(state);
+ show_safely();
+ told = tell();
+ idle_thread.notice(told);
+ } catch (ParseException pp) {
+ System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage());
+ } catch (AltosCRCException ce) {
+ ++listener_state.crc_errors;
+ show_safely();
+ }
+ }
+ } catch (InterruptedException ee) {
+ interrupted = true;
+ } catch (IOException ie) {
+ reading_error_safely();
+ } finally {
+ if (!interrupted)
+ idle_thread.report(true);
+ reader.close(interrupted);
+ idle_thread.interrupt();
+ try {
+ idle_thread.join();
+ } catch (InterruptedException ie) {}
+ }
+ }
+
+ public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) {
+ listener_state = new AltosListenerState();
+ parent = in_parent;
+ voice = in_voice;
+ display = in_display;
+ reader = in_reader;
+ display.reset();
+ }
+}
diff --git a/altosuilib/AltosEepromDelete.java b/altosuilib/AltosEepromDelete.java
new file mode 100644
index 00000000..981daddf
--- /dev/null
+++ b/altosuilib/AltosEepromDelete.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosEepromDelete implements Runnable {
+ AltosEepromList flights;
+ Thread eeprom_thread;
+ AltosSerial serial_line;
+ boolean remote;
+ JFrame frame;
+ ActionListener listener;
+ boolean success;
+
+ private void DeleteLog (AltosEepromLog log)
+ throws IOException, InterruptedException, TimeoutException {
+
+ if (flights.config_data.flight_log_max != 0 || flights.config_data.log_format != 0) {
+
+ /* Devices with newer firmware can erase the
+ * flash blocks containing each flight
+ */
+ serial_line.flush_input();
+ serial_line.printf("d %d\n", log.flight);
+ for (;;) {
+ /* It can take a while to erase the flash... */
+ String line = serial_line.get_reply(20000);
+ if (line == null)
+ throw new TimeoutException();
+ if (line.equals("Erased"))
+ break;
+ if (line.startsWith("No such"))
+ throw new IOException(line);
+ }
+ }
+ }
+
+ private void show_error_internal(String message, String title) {
+ JOptionPane.showMessageDialog(frame,
+ message,
+ title,
+ JOptionPane.ERROR_MESSAGE);
+ }
+
+ private void show_error(String in_message, String in_title) {
+ final String message = in_message;
+ final String title = in_title;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ show_error_internal(message, title);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ public void run () {
+ success = false;
+ try {
+ if (remote)
+ serial_line.start_remote();
+
+ for (AltosEepromLog log : flights) {
+ if (log.selected) {
+ DeleteLog(log);
+ }
+ }
+ success = true;
+ } catch (IOException ee) {
+ show_error (ee.getLocalizedMessage(),
+ serial_line.device.toShortString());
+ } catch (InterruptedException ie) {
+ } catch (TimeoutException te) {
+ show_error (String.format("Connection to \"%s\" failed",
+ serial_line.device.toShortString()),
+ "Connection Failed");
+ } finally {
+ try {
+ if (remote)
+ serial_line.stop_remote();
+ } catch (InterruptedException ie) {
+ } finally {
+ serial_line.flush_output();
+ serial_line.close();
+ }
+ }
+ if (listener != null) {
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ listener.actionPerformed(new ActionEvent(this,
+ success ? 1 : 0,
+ "delete"));
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+ }
+
+ public void start() {
+ eeprom_thread = new Thread(this);
+ eeprom_thread.start();
+ }
+
+ public void addActionListener(ActionListener l) {
+ listener = l;
+ }
+
+ public AltosEepromDelete(JFrame given_frame,
+ AltosSerial given_serial_line,
+ boolean given_remote,
+ AltosEepromList given_flights) {
+ frame = given_frame;
+ serial_line = given_serial_line;
+ remote = given_remote;
+ flights = given_flights;
+ success = false;
+ }
+}
diff --git a/altosuilib/AltosEepromManage.java b/altosuilib/AltosEepromManage.java
new file mode 100644
index 00000000..2b967339
--- /dev/null
+++ b/altosuilib/AltosEepromManage.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosEepromManage implements ActionListener {
+
+ JFrame frame;
+ boolean remote;
+ AltosDevice device;
+ AltosSerial serial_line;
+ AltosEepromList flights;
+ AltosEepromDownload download;
+ AltosEepromDelete delete;
+
+ public void finish() {
+ if (serial_line != null) {
+ try {
+ serial_line.flush_input();
+ } catch (InterruptedException ie) {
+ }
+ serial_line.close();
+ serial_line = null;
+ }
+ }
+
+ private int countDeletedFlights() {
+ int count = 0;
+ for (AltosEepromLog flight : flights) {
+ if (flight.selected)
+ count++;
+ }
+ return count;
+ }
+
+ private String showDeletedFlights() {
+ String result = "";
+
+ for (AltosEepromLog flight : flights) {
+ if (flight.selected) {
+ if (result.equals(""))
+ result = String.format("%d", flight.flight);
+ else
+ result = String.format("%s, %d", result, flight.flight);
+ }
+ }
+ return result;
+ }
+
+ public boolean download_done() {
+ AltosEepromSelect select = new AltosEepromSelect(frame, flights, "Delete");
+
+ if (select.run()) {
+ boolean any_selected = false;
+ for (AltosEepromLog flight : flights)
+ any_selected = any_selected || flight.selected;
+ if (any_selected) {
+ delete = new AltosEepromDelete(frame,
+ serial_line,
+ remote,
+ flights);
+ delete.addActionListener(this);
+ /*
+ * Start flight log delete
+ */
+
+ delete.start();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+ boolean success = e.getID() != 0;
+ boolean running = false;
+
+ if (cmd.equals("download")) {
+ if (success)
+ running = download_done();
+ } else if (cmd.equals("delete")) {
+ if (success) {
+ JOptionPane.showMessageDialog(frame,
+ String.format("%d flights erased: %s",
+ countDeletedFlights(),
+ showDeletedFlights()),
+ serial_line.device.toShortString(),
+ JOptionPane.INFORMATION_MESSAGE);
+ }
+ }
+ if (!running)
+ finish();
+ }
+
+ public void got_flights(AltosEepromList in_flights) {
+ boolean running = false;;
+
+ flights = in_flights;
+ try {
+ if (flights.size() == 0) {
+ JOptionPane.showMessageDialog(frame,
+ String.format("No flights available on %d",
+ device.getSerial()),
+ serial_line.device.toShortString(),
+ JOptionPane.INFORMATION_MESSAGE);
+ } else {
+ AltosEepromSelect select = new AltosEepromSelect(frame, flights, "Download");
+
+ if (select.run()) {
+ boolean any_selected = false;
+ for (AltosEepromLog flight : flights)
+ any_selected = any_selected || flight.selected;
+ if (any_selected) {
+ AltosEepromMonitorUI monitor = new AltosEepromMonitorUI(frame);
+ monitor.addActionListener(this);
+ serial_line.set_frame(frame);
+ download = new AltosEepromDownload(monitor,
+ serial_line,
+ remote,
+ flights);
+ /*
+ * Start flight log download
+ */
+
+ download.start();
+ running = true;
+ } else {
+ running = download_done();
+ }
+ }
+ }
+ if (!running)
+ finish();
+ } catch (Exception e) {
+ got_exception(e);
+ }
+ }
+
+ public void got_exception(Exception e) {
+ if (e instanceof IOException) {
+ IOException ee = (IOException) e;
+ JOptionPane.showMessageDialog(frame,
+ device.toShortString(),
+ ee.getLocalizedMessage(),
+ JOptionPane.ERROR_MESSAGE);
+ } else if (e instanceof TimeoutException) {
+ //TimeoutException te = (TimeoutException) e;
+ JOptionPane.showMessageDialog(frame,
+ String.format("Communications failed with \"%s\"",
+ device.toShortString()),
+ "Cannot open target device",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ finish();
+ }
+
+ class EepromGetList implements Runnable {
+
+ AltosEepromManage manage;
+
+ public void run () {
+ Runnable r;
+ try {
+ flights = new AltosEepromList(serial_line, remote);
+ r = new Runnable() {
+ public void run() {
+ got_flights(flights);
+ }
+ };
+ } catch (Exception e) {
+ final Exception f_e = e;
+ r = new Runnable() {
+ public void run() {
+ got_exception(f_e);
+ }
+ };
+ }
+ SwingUtilities.invokeLater(r);
+ }
+
+ public EepromGetList(AltosEepromManage in_manage) {
+ manage = in_manage;
+ }
+ }
+
+ public AltosEepromManage(JFrame given_frame, int product) {
+
+ //boolean running = false;
+
+ frame = given_frame;
+ device = AltosDeviceUIDialog.show(frame, product);
+
+ remote = false;
+
+ if (device != null) {
+ try {
+ serial_line = new AltosSerial(device);
+ if (device.matchProduct(AltosLib.product_basestation))
+ remote = true;
+
+ serial_line.set_frame(frame);
+
+ EepromGetList get_list = new EepromGetList(this);
+ Thread t = new Thread(get_list);
+ t.start();
+ } catch (FileNotFoundException ee) {
+ JOptionPane.showMessageDialog(frame,
+ ee.getMessage(),
+ "Cannot open target device",
+ JOptionPane.ERROR_MESSAGE);
+ } catch (AltosSerialInUseException si) {
+ JOptionPane.showMessageDialog(frame,
+ String.format("Device \"%s\" already in use",
+ device.toShortString()),
+ "Device in use",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+}
diff --git a/altosuilib/AltosEepromMonitor.java b/altosuilib/AltosEepromMonitor.java
new file mode 100644
index 00000000..b1e85622
--- /dev/null
+++ b/altosuilib/AltosEepromMonitor.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+public class AltosEepromMonitor extends AltosUIDialog {
+
+ Container pane;
+ Box box;
+ JLabel serial_label;
+ JLabel flight_label;
+ JLabel file_label;
+ JLabel serial_value;
+ JLabel flight_value;
+ JLabel file_value;
+ JButton cancel;
+ JProgressBar pbar;
+ int min_state, max_state;
+
+ public AltosEepromMonitor(JFrame owner, int in_min_state, int in_max_state) {
+ super (owner, "Download Flight Data", false);
+
+ GridBagConstraints c;
+ Insets il = new Insets(4,4,4,4);
+ Insets ir = new Insets(4,4,4,4);
+
+ pane = getContentPane();
+ pane.setLayout(new GridBagLayout());
+
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = 0;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ serial_label = new JLabel("Serial:");
+ pane.add(serial_label, c);
+
+ c = new GridBagConstraints();
+ c.gridx = 1; c.gridy = 0;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ serial_value = new JLabel("");
+ pane.add(serial_value, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.NONE;
+ c.gridx = 0; c.gridy = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ flight_label = new JLabel("Flight:");
+ pane.add(flight_label, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.gridx = 1; c.gridy = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ flight_value = new JLabel("");
+ pane.add(flight_value, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.NONE;
+ c.gridx = 0; c.gridy = 2;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ file_label = new JLabel("File:");
+ pane.add(file_label, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.gridx = 1; c.gridy = 2;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ file_value = new JLabel("");
+ pane.add(file_value, c);
+
+ min_state = in_min_state;
+ max_state = in_max_state;
+ pbar = new JProgressBar();
+ pbar.setMinimum(0);
+ pbar.setMaximum(1000);
+ pbar.setValue(0);
+ pbar.setString("startup");
+ pbar.setStringPainted(true);
+ pbar.setPreferredSize(new Dimension(600, 20));
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.CENTER;
+ c.gridx = 0; c.gridy = 3;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ Insets ib = new Insets(4,4,4,4);
+ c.insets = ib;
+ pane.add(pbar, c);
+
+
+ cancel = new JButton("Cancel");
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.gridx = 0; c.gridy = 4;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ Insets ic = new Insets(4,4,4,4);
+ c.insets = ic;
+ pane.add(cancel, c);
+
+ pack();
+ setLocationRelativeTo(owner);
+ setVisible(true);
+ }
+
+ public void addActionListener (ActionListener l) {
+ cancel.addActionListener(l);
+ }
+
+ private void set_value_internal(String state_name, int state, int state_block, int block) {
+ if (state_block > 100)
+ state_block = 100;
+ if (state < min_state) state = min_state;
+ if (state >= max_state) state = max_state - 1;
+ state -= min_state;
+
+ int pos = state * 100 + state_block;
+
+ pbar.setString(String.format("block %d state %s", block, state_name));
+ pbar.setValue(pos);
+ }
+
+ public void set_value(String in_state_name, int in_state, int in_state_block, int in_block) {
+ final String state_name = in_state_name;
+ final int state = in_state;
+ final int state_block = in_state_block;
+ final int block = in_block;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ set_value_internal(state_name, state, state_block, block);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void set_serial_internal(int serial) {
+ serial_value.setText(String.format("%d", serial));
+ }
+
+ public void set_serial(int in_serial) {
+ final int serial = in_serial;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ set_serial_internal(serial);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void set_flight_internal(int flight) {
+ flight_value.setText(String.format("%d", flight));
+ }
+
+ public void set_flight(int in_flight) {
+ final int flight = in_flight;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ set_flight_internal(flight);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void set_file_internal(String file) {
+ file_value.setText(String.format("%s", file));
+ }
+
+ public void set_file(String in_file) {
+ final String file = in_file;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ set_file_internal(file);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void done_internal() {
+ setVisible(false);
+ dispose();
+ }
+
+ public void done() {
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ done_internal();
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void reset_internal() {
+ set_value_internal("startup",min_state,0, 0);
+ set_flight_internal(0);
+ set_file_internal("");
+ }
+
+ public void reset() {
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ reset_internal();
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+}
diff --git a/altosuilib/AltosEepromMonitorUI.java b/altosuilib/AltosEepromMonitorUI.java
new file mode 100644
index 00000000..02c71cd9
--- /dev/null
+++ b/altosuilib/AltosEepromMonitorUI.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosEepromMonitorUI extends AltosUIDialog implements AltosEepromMonitor {
+ JFrame owner;
+ Container pane;
+ Box box;
+ JLabel serial_label;
+ JLabel flight_label;
+ JLabel file_label;
+ JLabel serial_value;
+ JLabel flight_value;
+ JLabel file_value;
+ JButton cancel;
+ JProgressBar pbar;
+ int min_state, max_state;
+ ActionListener listener;
+
+ public AltosEepromMonitorUI(JFrame owner) {
+ super (owner, "Download Flight Data", false);
+
+ this.owner = owner;
+
+ GridBagConstraints c;
+ Insets il = new Insets(4,4,4,4);
+ Insets ir = new Insets(4,4,4,4);
+
+ pane = getContentPane();
+ pane.setLayout(new GridBagLayout());
+
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = 0;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ serial_label = new JLabel("Serial:");
+ pane.add(serial_label, c);
+
+ c = new GridBagConstraints();
+ c.gridx = 1; c.gridy = 0;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ serial_value = new JLabel("");
+ pane.add(serial_value, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.NONE;
+ c.gridx = 0; c.gridy = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ flight_label = new JLabel("Flight:");
+ pane.add(flight_label, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.gridx = 1; c.gridy = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ flight_value = new JLabel("");
+ pane.add(flight_value, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.NONE;
+ c.gridx = 0; c.gridy = 2;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ file_label = new JLabel("File:");
+ pane.add(file_label, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.gridx = 1; c.gridy = 2;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ file_value = new JLabel("");
+ pane.add(file_value, c);
+
+ pbar = new JProgressBar();
+ pbar.setMinimum(0);
+ pbar.setMaximum(1000);
+ pbar.setValue(0);
+ pbar.setString("startup");
+ pbar.setStringPainted(true);
+ pbar.setPreferredSize(new Dimension(600, 20));
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.CENTER;
+ c.gridx = 0; c.gridy = 3;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ Insets ib = new Insets(4,4,4,4);
+ c.insets = ib;
+ pane.add(pbar, c);
+
+
+ cancel = new JButton("Cancel");
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.gridx = 0; c.gridy = 4;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ Insets ic = new Insets(4,4,4,4);
+ c.insets = ic;
+ pane.add(cancel, c);
+
+ pack();
+ setLocationRelativeTo(owner);
+ }
+
+ public void addActionListener(ActionListener l) {
+ listener = l;
+ }
+
+ public void set_states(int min_state, int max_state) {
+ this.min_state = min_state;
+ this.max_state = max_state;
+ }
+
+ public void set_thread(Thread in_eeprom_thread) {
+ final Thread eeprom_thread = in_eeprom_thread;
+ cancel.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (eeprom_thread != null)
+ eeprom_thread.interrupt();
+ }
+ });
+ }
+
+ public void start() {
+ setVisible(true);
+ }
+
+ private void set_value_internal(String state_name, int state, int state_block, int block) {
+ if (state_block > 100)
+ state_block = 100;
+ if (state < min_state) state = min_state;
+ if (state >= max_state) state = max_state - 1;
+ state -= min_state;
+
+ int pos = state * 100 + state_block;
+
+ pbar.setString(String.format("block %d state %s", block, state_name));
+ pbar.setValue(pos);
+ }
+
+ public void set_value(String in_state_name, int in_state, int in_state_block, int in_block) {
+ final String state_name = in_state_name;
+ final int state = in_state;
+ final int state_block = in_state_block;
+ final int block = in_block;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ set_value_internal(state_name, state, state_block, block);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void set_serial_internal(int serial) {
+ serial_value.setText(String.format("%d", serial));
+ }
+
+ public void set_serial(int in_serial) {
+ final int serial = in_serial;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ set_serial_internal(serial);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void set_flight_internal(int flight) {
+ flight_value.setText(String.format("%d", flight));
+ }
+
+ public void set_flight(int in_flight) {
+ final int flight = in_flight;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ set_flight_internal(flight);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void set_filename_internal(String filename) {
+ file_value.setText(String.format("%s", filename));
+ }
+
+ public void set_filename(String in_filename) {
+ final String filename = in_filename;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ set_filename_internal(filename);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void done_internal(boolean success) {
+ listener.actionPerformed(new ActionEvent(this,
+ success ? 1 : 0,
+ "download"));
+ setVisible(false);
+ dispose();
+ }
+
+ public void done(boolean in_success) {
+ final boolean success = in_success;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ done_internal(success);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void reset_internal() {
+ set_value_internal("startup",min_state,0, 0);
+ set_flight_internal(0);
+ set_filename_internal("");
+ }
+
+ public void reset() {
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ reset_internal();
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private void show_message_internal(String message, String title, int message_type) {
+ int joption_message_type = JOptionPane.ERROR_MESSAGE;
+
+ switch (message_type) {
+ case INFO_MESSAGE:
+ joption_message_type = JOptionPane.INFORMATION_MESSAGE;
+ break;
+ case WARNING_MESSAGE:
+ joption_message_type = JOptionPane.WARNING_MESSAGE;
+ break;
+ case ERROR_MESSAGE:
+ joption_message_type = JOptionPane.ERROR_MESSAGE;
+ break;
+ }
+ JOptionPane.showMessageDialog(owner,
+ message,
+ title,
+ joption_message_type);
+ }
+
+ public void show_message(String in_message, String in_title, int in_message_type) {
+ final String message = in_message;
+ final String title = in_title;
+ final int message_type = in_message_type;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ show_message_internal(message, title, message_type);
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+}
diff --git a/altosuilib/AltosEepromSelect.java b/altosuilib/AltosEepromSelect.java
new file mode 100644
index 00000000..293d3045
--- /dev/null
+++ b/altosuilib/AltosEepromSelect.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import java.awt.*;
+import java.awt.event.*;
+import org.altusmetrum.altoslib_4.*;
+
+class AltosEepromItem implements ActionListener {
+ AltosEepromLog log;
+ JLabel label;
+ JCheckBox action;
+ JCheckBox delete;
+
+ public void actionPerformed(ActionEvent e) {
+ log.selected = action.isSelected();
+ }
+
+ public AltosEepromItem(AltosEepromLog in_log) {
+ log = in_log;
+
+ String text;
+ if (log.year != 0)
+ text = String.format("Flight #%02d - %04d-%02d-%02d",
+ log.flight, log.year, log.month, log.day);
+ else
+ text = String.format("Flight #%02d", log.flight);
+
+ label = new JLabel(text);
+
+ action = new JCheckBox("", log.selected);
+ action.addActionListener(this);
+ }
+}
+
+public class AltosEepromSelect extends AltosUIDialog implements ActionListener {
+ //private JList list;
+ private JFrame frame;
+ JButton ok;
+ JButton cancel;
+ boolean success;
+
+ /* Listen for events from our buttons */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+
+ if (cmd.equals("ok"))
+ success = true;
+ setVisible(false);
+ }
+
+ public boolean run() {
+ success = false;
+ setLocationRelativeTo(frame);
+ setVisible(true);
+ return success;
+ }
+
+ public AltosEepromSelect (JFrame in_frame,
+ AltosEepromList flights,
+ String action) {
+
+ super(in_frame, String.format("Flight list for serial %d", flights.config_data.serial), true);
+ frame = in_frame;
+
+ /* Create the container for the dialog */
+ Container contentPane = getContentPane();
+
+ /* First, we create a pane containing the dialog's header/title */
+ JLabel selectLabel = new JLabel(String.format ("Select flights to %s", action), SwingConstants.CENTER);
+
+ JPanel labelPane = new JPanel();
+ labelPane.setLayout(new BoxLayout(labelPane, BoxLayout.X_AXIS));
+ labelPane.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
+ labelPane.add(Box.createHorizontalGlue());
+ labelPane.add(selectLabel);
+ labelPane.add(Box.createHorizontalGlue());
+
+ /* Add the header to the container. */
+ contentPane.add(labelPane, BorderLayout.PAGE_START);
+
+
+ /* Now we create the evilness that is a GridBag for the flight details */
+ GridBagConstraints c;
+ Insets i = new Insets(4,4,4,4);
+ JPanel flightPane = new JPanel();
+ flightPane.setLayout(new GridBagLayout());
+ flightPane.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
+
+ /* Flight Header */
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = 0;
+ c.fill = GridBagConstraints.NONE;
+ c.weightx = 0.5;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ JLabel flightHeaderLabel = new JLabel("Flight");
+ flightPane.add(flightHeaderLabel, c);
+
+ /* Download Header */
+ c = new GridBagConstraints();
+ c.gridx = 1; c.gridy = 0;
+ c.fill = GridBagConstraints.NONE;
+ c.weightx = 0.5;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ JLabel downloadHeaderLabel = new JLabel(action);
+ flightPane.add(downloadHeaderLabel, c);
+
+ /* Add the flights to the GridBag */
+ AltosEepromItem item;
+ int itemNumber = 1;
+ for (AltosEepromLog flight : flights) {
+ /* Create a flight object with handlers and
+ * appropriate UI items
+ */
+ item = new AltosEepromItem(flight);
+
+ /* Add a decriptive label for the flight */
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = itemNumber;
+ c.fill = GridBagConstraints.NONE;
+ c.weightx = 0.5;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ flightPane.add(item.label, c);
+
+ /* Add action checkbox for the flight */
+ c = new GridBagConstraints();
+ c.gridx = 1; c.gridy = itemNumber;
+ c.fill = GridBagConstraints.NONE;
+ c.weightx = 0.5;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ flightPane.add(item.action, c);
+
+ itemNumber++;
+ }
+
+ /* Add the GridBag to the container */
+ contentPane.add(flightPane, BorderLayout.CENTER);
+
+ /* Create the dialog buttons */
+ ok = new JButton("OK");
+ ok.addActionListener(this);
+ ok.setActionCommand("ok");
+
+ cancel = new JButton("Cancel");
+ cancel.addActionListener(this);
+ cancel.setActionCommand("cancel");
+
+ JPanel buttonPane = new JPanel();
+ buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
+ buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ buttonPane.add(Box.createHorizontalGlue());
+ buttonPane.add(cancel);
+ buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+ buttonPane.add(ok);
+
+ /* Add the buttons to the container */
+ contentPane.add(buttonPane, BorderLayout.PAGE_END);
+
+ /* Pack the window! */
+ pack();
+ }
+}
diff --git a/altosuilib/AltosFlashUI.java b/altosuilib/AltosFlashUI.java
new file mode 100644
index 00000000..3f120617
--- /dev/null
+++ b/altosuilib/AltosFlashUI.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import java.io.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosFlashUI
+ extends AltosUIDialog
+ implements ActionListener
+{
+ Container pane;
+ Box box;
+ JLabel serial_label;
+ JLabel serial_value;
+ JLabel file_label;
+ JLabel file_value;
+ JProgressBar pbar;
+ JButton cancel;
+
+ AltosUIFrame frame;
+
+ // Hex file with rom image
+ File file;
+
+ // Debug connection
+ AltosDevice device;
+
+ AltosLink link;
+
+ // Desired Rom configuration
+ AltosRomconfig rom_config;
+
+ // Flash controller
+ AltosProgrammer programmer;
+
+ private static String[] pair_programmed = {
+ "teleballoon",
+ "telebt",
+ "teledongle",
+ "telefire",
+ "telemetrum-v0",
+ "telemetrum-v1",
+ "telemini",
+ "telenano",
+ "teleshield",
+ "teleterra"
+ };
+
+ private boolean is_pair_programmed() {
+
+ if (file != null) {
+ String name = file.getName();
+ for (int i = 0; i < pair_programmed.length; i++) {
+ if (name.startsWith(pair_programmed[i]))
+ return true;
+ }
+ }
+ if (device != null) {
+ if (!device.matchProduct(AltosLib.product_altusmetrum) &&
+ (device.matchProduct(AltosLib.product_teledongle) ||
+ device.matchProduct(AltosLib.product_telebt)))
+ return true;
+ }
+ return false;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource() == cancel) {
+ if (programmer != null)
+ programmer.abort();
+ setVisible(false);
+ dispose();
+ } else {
+ String cmd = e.getActionCommand();
+ if (e.getID() == -1) {
+ JOptionPane.showMessageDialog(frame,
+ e.getActionCommand(),
+ file.toString(),
+ JOptionPane.ERROR_MESSAGE);
+ setVisible(false);
+ dispose();
+ } else if (cmd.equals("done")) {
+ setVisible(false);
+ dispose();
+ } else if (cmd.equals("start")) {
+ setVisible(true);
+ } else {
+ pbar.setValue(e.getID());
+ pbar.setString(cmd);
+ }
+ }
+ }
+
+ public void build_dialog() {
+ GridBagConstraints c;
+ Insets il = new Insets(4,4,4,4);
+ Insets ir = new Insets(4,4,4,4);
+
+ pane = getContentPane();
+ pane.setLayout(new GridBagLayout());
+
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = 0;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ serial_label = new JLabel("Serial:");
+ pane.add(serial_label, c);
+
+ c = new GridBagConstraints();
+ c.gridx = 1; c.gridy = 0;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ serial_value = new JLabel("");
+ pane.add(serial_value, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.NONE;
+ c.gridx = 0; c.gridy = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ file_label = new JLabel("File:");
+ pane.add(file_label, c);
+
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.gridx = 1; c.gridy = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ file_value = new JLabel(file.toString());
+ pane.add(file_value, c);
+
+ pbar = new JProgressBar();
+ pbar.setMinimum(0);
+ pbar.setMaximum(100);
+ pbar.setValue(0);
+ pbar.setString("");
+ pbar.setStringPainted(true);
+ pbar.setPreferredSize(new Dimension(600, 20));
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.CENTER;
+ c.gridx = 0; c.gridy = 2;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ Insets ib = new Insets(4,4,4,4);
+ c.insets = ib;
+ pane.add(pbar, c);
+
+ cancel = new JButton("Cancel");
+ c = new GridBagConstraints();
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.gridx = 0; c.gridy = 3;
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ Insets ic = new Insets(4,4,4,4);
+ c.insets = ic;
+ pane.add(cancel, c);
+ cancel.addActionListener(this);
+ pack();
+ setLocationRelativeTo(frame);
+ }
+
+ void set_serial(int serial_number) {
+ serial_value.setText(String.format("%d", serial_number));
+ }
+
+ static class AltosHexfileFilter extends javax.swing.filechooser.FileFilter {
+ int product;
+ String head;
+ String description;
+
+ public AltosHexfileFilter(int product, String head, String description) {
+ this.product = product;
+ this.head = head;
+ this.description = description;
+ }
+
+ public boolean accept(File file) {
+ return !file.isFile() || (file.getName().startsWith(head) && file.getName().endsWith(".ihx"));
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ }
+
+ static AltosHexfileFilter[] filters = {
+ new AltosHexfileFilter(AltosLib.product_telemetrum, "telemetrum", "TeleMetrum Image"),
+ new AltosHexfileFilter(AltosLib.product_teledongle, "teledongle", "TeleDongle Image"),
+ new AltosHexfileFilter(AltosLib.product_telemega, "telemega", "TeleMega Image"),
+ new AltosHexfileFilter(AltosLib.product_easymini, "easymini", "EasyMini Image"),
+ };
+
+ boolean select_source_file() {
+ JFileChooser hexfile_chooser = new JFileChooser();
+
+ File firmwaredir = AltosUIPreferences.firmwaredir();
+ if (firmwaredir != null)
+ hexfile_chooser.setCurrentDirectory(firmwaredir);
+
+ hexfile_chooser.setDialogTitle("Select Flash Image");
+
+ for (int i = 0; i < filters.length; i++) {
+ hexfile_chooser.addChoosableFileFilter(filters[i]);
+ }
+ javax.swing.filechooser.FileFilter ihx_filter = new FileNameExtensionFilter("Flash Image", "ihx");
+ hexfile_chooser.addChoosableFileFilter(ihx_filter);
+ hexfile_chooser.setFileFilter(ihx_filter);
+
+ if (!is_pair_programmed() && !device.matchProduct(AltosLib.product_altusmetrum)) {
+ for (int i = 0; i < filters.length; i++) {
+ if (device != null && device.matchProduct(filters[i].product))
+ hexfile_chooser.setFileFilter(filters[i]);
+ }
+ }
+
+ int returnVal = hexfile_chooser.showOpenDialog(frame);
+
+ if (returnVal != JFileChooser.APPROVE_OPTION)
+ return false;
+ file = hexfile_chooser.getSelectedFile();
+ if (file == null)
+ return false;
+ AltosUIPreferences.set_firmwaredir(file.getParentFile());
+
+ return true;
+ }
+
+ boolean select_device() {
+ int product = AltosLib.product_any;
+
+ device = AltosDeviceUIDialog.show(frame, AltosLib.product_any);
+
+ if (device == null)
+ return false;
+ return true;
+ }
+
+ boolean update_rom_config_info(AltosRomconfig existing_config) {
+ AltosRomconfig new_config;
+ new_config = AltosRomconfigUI.show(frame, existing_config);
+ if (new_config == null)
+ return false;
+ rom_config = new_config;
+ set_serial(rom_config.serial_number);
+ setVisible(true);
+ return true;
+ }
+
+ void exception (Exception e) {
+ if (e instanceof FileNotFoundException) {
+ JOptionPane.showMessageDialog(frame,
+ ((FileNotFoundException) e).getMessage(),
+ "Cannot open file",
+ JOptionPane.ERROR_MESSAGE);
+ } else if (e instanceof AltosSerialInUseException) {
+ JOptionPane.showMessageDialog(frame,
+ String.format("Device \"%s\" already in use",
+ device.toShortString()),
+ "Device in use",
+ JOptionPane.ERROR_MESSAGE);
+ } else if (e instanceof IOException) {
+ JOptionPane.showMessageDialog(frame,
+ e.getMessage(),
+ file.toString(),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ class flash_task implements Runnable, AltosFlashListener {
+ AltosFlashUI ui;
+ Thread t;
+ AltosProgrammer programmer;
+
+ public void position(String in_s, int in_percent) {
+ final String s = in_s;
+ final int percent = in_percent;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ ui.actionPerformed(new ActionEvent(this,
+ percent,
+ s));
+ } catch (Exception ex) {
+ }
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ public void run () {
+ try {
+ if (ui.is_pair_programmed())
+ programmer = new AltosFlash(ui.file, link, this);
+ else
+ programmer = new AltosSelfFlash(ui.file, link, this);
+
+ final AltosRomconfig current_config = programmer.romconfig();
+
+ final Semaphore await_rom_config = new Semaphore(0);
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ ui.programmer = programmer;
+ ui.update_rom_config_info(current_config);
+ await_rom_config.release();
+ }
+ });
+ await_rom_config.acquire();
+
+ if (ui.rom_config != null) {
+ programmer.set_romconfig(ui.rom_config);
+ programmer.flash();
+ }
+ } catch (InterruptedException ee) {
+ final Exception e = ee;
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ ui.exception(e);
+ }
+ });
+ } catch (IOException ee) {
+ final Exception e = ee;
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ ui.exception(e);
+ }
+ });
+ } finally {
+ if (programmer != null)
+ programmer.close();
+ }
+ }
+
+ public flash_task(AltosFlashUI in_ui) {
+ ui = in_ui;
+ t = new Thread(this);
+ t.start();
+ }
+ }
+
+ flash_task flasher;
+
+ private boolean open_device() throws InterruptedException {
+ try {
+ link = new AltosSerial(device);
+ if (is_pair_programmed())
+ return true;
+
+ if (link == null)
+ throw new IOException(String.format("%s: open failed", device.toShortString()));
+
+ while (!link.is_loader()) {
+ link.to_loader();
+
+ java.util.List<AltosDevice> devices = null;
+
+ for (int tries = 0; tries < 10; tries++) {
+ Thread.sleep(100);
+ devices = AltosUSBDevice.list(AltosLib.product_altusmetrum);
+ if (devices.size() != 0)
+ break;
+ }
+
+ if (devices.size() == 1)
+ device = devices.get(0);
+ else {
+ device = AltosDeviceUIDialog.show(frame, AltosLib.product_altusmetrum);
+ if (device == null)
+ return false;
+ }
+ link = new AltosSerial(device);
+ }
+ return true;
+ } catch (AltosSerialInUseException ee) {
+ exception(ee);
+ } catch (FileNotFoundException fe) {
+ exception(fe);
+ } catch (IOException ie) {
+ exception (ie);
+ }
+ return false;
+ }
+
+ /*
+ * Execute the steps for flashing
+ * a device. Note that this returns immediately;
+ * this dialog is not modal
+ */
+ void showDialog() {
+ if (!select_device())
+ return;
+ if (!select_source_file())
+ return;
+ try {
+ if (!open_device())
+ return;
+ } catch (InterruptedException ie) {
+ return;
+ }
+ build_dialog();
+ flash_task f = new flash_task(this);
+ }
+
+ public static void show(AltosUIFrame frame) {
+ AltosFlashUI ui = new AltosFlashUI(frame);
+ ui.showDialog();
+ }
+
+ public AltosFlashUI(AltosUIFrame in_frame) {
+ super(in_frame, "Program Altusmetrum Device", false);
+
+ frame = in_frame;
+ }
+}
diff --git a/altosuilib/AltosFlightDisplay.java b/altosuilib/AltosFlightDisplay.java
new file mode 100644
index 00000000..55b74034
--- /dev/null
+++ b/altosuilib/AltosFlightDisplay.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import org.altusmetrum.altoslib_4.*;
+
+public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener {
+ void reset();
+
+ void show(AltosState state, AltosListenerState listener_state);
+
+ String getName();
+}
diff --git a/altosuilib/AltosFlightInfoTableModel.java b/altosuilib/AltosFlightInfoTableModel.java
new file mode 100644
index 00000000..3995efb3
--- /dev/null
+++ b/altosuilib/AltosFlightInfoTableModel.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import javax.swing.table.*;
+
+public class AltosFlightInfoTableModel extends AbstractTableModel {
+ final static private String[] columnNames = {"Field", "Value"};
+
+ int rows;
+ int cols;
+ private String[][] data;
+
+ public int getColumnCount() { return cols; }
+ public int getRowCount() { return rows; }
+ public String getColumnName(int col) { return columnNames[col & 1]; }
+
+ public Object getValueAt(int row, int col) {
+ if (row >= rows || col >= cols)
+ return "";
+ return data[row][col];
+ }
+
+ int[] current_row;
+
+ public void reset() {
+ for (int i = 0; i < cols / 2; i++)
+ current_row[i] = 0;
+ }
+
+ public void clear() {
+ reset();
+ for (int c = 0; c < cols; c++)
+ for (int r = 0; r < rows; r++)
+ data[r][c] = "";
+ fireTableDataChanged();
+ }
+
+ public void addRow(int col, String name, String value) {
+ if (current_row[col] < rows) {
+ data[current_row[col]][col * 2] = name;
+ data[current_row[col]][col * 2 + 1] = value;
+ }
+ current_row[col]++;
+ }
+
+ public void finish() {
+ for (int c = 0; c < cols / 2; c++)
+ while (current_row[c] < rows)
+ addRow(c, "", "");
+ fireTableDataChanged();
+ }
+
+ public AltosFlightInfoTableModel (int in_rows, int in_cols) {
+ rows = in_rows;
+ cols = in_cols * 2;
+ data = new String[rows][cols];
+ current_row = new int[in_cols];
+ }
+}
diff --git a/altosuilib/AltosFlightStatsTable.java b/altosuilib/AltosFlightStatsTable.java
new file mode 100644
index 00000000..703dfb9d
--- /dev/null
+++ b/altosuilib/AltosFlightStatsTable.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import javax.swing.*;
+import java.util.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosFlightStatsTable extends JComponent implements AltosFontListener {
+ GridBagLayout layout;
+
+ LinkedList<FlightStat> flight_stats = new LinkedList<FlightStat>();
+
+ class FlightStat implements AltosFontListener {
+ JLabel label;
+ JTextField[] value;
+
+ public void font_size_changed(int font_size) {
+ label.setFont(AltosUILib.label_font);
+ for (int i = 0; i < value.length; i++)
+ value[i].setFont(AltosUILib.value_font);
+ }
+
+ public FlightStat(GridBagLayout layout, int y, String label_text, String ... values) {
+ GridBagConstraints c = new GridBagConstraints();
+ c.insets = new Insets(AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad);
+ c.weighty = 1;
+
+ label = new JLabel(label_text);
+ label.setFont(AltosUILib.label_font);
+ label.setHorizontalAlignment(SwingConstants.LEFT);
+ c.gridx = 0; c.gridy = y;
+ c.anchor = GridBagConstraints.WEST;
+ c.fill = GridBagConstraints.VERTICAL;
+ c.weightx = 0;
+ layout.setConstraints(label, c);
+ add(label);
+
+ value = new JTextField[values.length];
+ for (int j = 0; j < values.length; j++) {
+ value[j] = new JTextField(values[j]);
+ value[j].setEditable(false);
+ value[j].setFont(AltosUILib.value_font);
+ value[j].setHorizontalAlignment(SwingConstants.RIGHT);
+ c.gridx = j+1; c.gridy = y;
+ c.anchor = GridBagConstraints.EAST;
+ c.fill = GridBagConstraints.BOTH;
+ c.weightx = 1;
+ layout.setConstraints(value[j], c);
+ add(value[j]);
+ }
+ flight_stats.add(this);
+ }
+
+ }
+
+ public void font_size_changed(int font_size) {
+ for (FlightStat f : flight_stats)
+ f.font_size_changed(font_size);
+ }
+
+ static String pos(double p, String pos, String neg) {
+ String h = pos;
+ if (p < 0) {
+ h = neg;
+ p = -p;
+ }
+ int deg = (int) Math.floor(p);
+ double min = (p - Math.floor(p)) * 60.0;
+ return String.format("%s %4d° %9.6f'", h, deg, min);
+ }
+
+ public AltosFlightStatsTable(AltosFlightStats stats) {
+ layout = new GridBagLayout();
+
+ setLayout(layout);
+ int y = 0;
+ new FlightStat(layout, y++, "Serial", String.format("%d", stats.serial));
+ new FlightStat(layout, y++, "Flight", String.format("%d", stats.flight));
+ if (stats.year != AltosLib.MISSING && stats.hour != AltosLib.MISSING)
+ new FlightStat(layout, y++, "Date/Time",
+ String.format("%04d-%02d-%02d", stats.year, stats.month, stats.day),
+ String.format("%02d:%02d:%02d UTC", stats.hour, stats.minute, stats.second));
+ else {
+ if (stats.year != AltosLib.MISSING)
+ new FlightStat(layout, y++, "Date",
+ String.format("%04d-%02d-%02d", stats.year, stats.month, stats.day));
+ if (stats.hour != AltosLib.MISSING)
+ new FlightStat(layout, y++, "Time",
+ String.format("%02d:%02d:%02d UTC", stats.hour, stats.minute, stats.second));
+ }
+ if (stats.max_height != AltosLib.MISSING) {
+ new FlightStat(layout, y++, "Maximum height",
+ String.format("%5.0f m", stats.max_height),
+ String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.max_height)));
+ }
+ if (stats.max_gps_height != AltosLib.MISSING) {
+ new FlightStat(layout, y++, "Maximum GPS height",
+ String.format("%5.0f m", stats.max_gps_height),
+ String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.max_gps_height)));
+ }
+ new FlightStat(layout, y++, "Maximum speed",
+ String.format("%5.0f m/s", stats.max_speed),
+ String.format("%5.0f mph", AltosConvert.meters_to_mph(stats.max_speed)),
+ String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed)));
+ if (stats.max_acceleration != AltosLib.MISSING) {
+ new FlightStat(layout, y++, "Maximum boost acceleration",
+ String.format("%5.0f m/s²", stats.max_acceleration),
+ String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_acceleration)),
+ String.format("%5.0f G", AltosConvert.meters_to_g(stats.max_acceleration)));
+ new FlightStat(layout, y++, "Average boost acceleration",
+ String.format("%5.0f m/s²", stats.state_accel[AltosLib.ao_flight_boost]),
+ String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.state_accel[AltosLib.ao_flight_boost])),
+ String.format("%5.0f G", AltosConvert.meters_to_g(stats.state_accel[AltosLib.ao_flight_boost])));
+ }
+ if (stats.state_speed[AltosLib.ao_flight_drogue] != AltosLib.MISSING)
+ new FlightStat(layout, y++, "Drogue descent rate",
+ String.format("%5.0f m/s", stats.state_speed[AltosLib.ao_flight_drogue]),
+ String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.state_speed[AltosLib.ao_flight_drogue])));
+ if (stats.state_speed[AltosLib.ao_flight_main] != AltosLib.MISSING)
+ new FlightStat(layout, y++, "Main descent rate",
+ String.format("%5.0f m/s", stats.state_speed[AltosLib.ao_flight_main]),
+ String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.state_speed[AltosLib.ao_flight_main])));
+ if (stats.state_start[AltosLib.ao_flight_boost] < stats.state_end[AltosLib.ao_flight_coast])
+ new FlightStat(layout, y++, "Ascent time",
+ String.format("%6.1f s %s", stats.state_end[AltosLib.ao_flight_boost] - stats.state_start[AltosLib.ao_flight_boost],
+ AltosLib.state_name(AltosLib.ao_flight_boost)),
+ String.format("%6.1f s %s", stats.state_end[AltosLib.ao_flight_fast] - stats.state_start[AltosLib.ao_flight_fast],
+ AltosLib.state_name(AltosLib.ao_flight_fast)),
+ String.format("%6.1f s %s", stats.state_end[AltosLib.ao_flight_coast] - stats.state_start[AltosLib.ao_flight_coast],
+ AltosLib.state_name(AltosLib.ao_flight_coast)));
+ if (stats.state_start[AltosLib.ao_flight_drogue] < stats.state_end[AltosLib.ao_flight_main])
+ new FlightStat(layout, y++, "Descent time",
+ String.format("%6.1f s %s", stats.state_end[AltosLib.ao_flight_drogue] - stats.state_start[AltosLib.ao_flight_drogue],
+ AltosLib.state_name(AltosLib.ao_flight_drogue)),
+ String.format("%6.1f s %s", stats.state_end[AltosLib.ao_flight_main] - stats.state_start[AltosLib.ao_flight_main],
+ AltosLib.state_name(AltosLib.ao_flight_main)));
+ if (stats.state_start[AltosLib.ao_flight_boost] < stats.state_end[AltosLib.ao_flight_main])
+ new FlightStat(layout, y++, "Flight time",
+ String.format("%6.1f s", stats.state_end[AltosLib.ao_flight_main] -
+ stats.state_start[AltosLib.ao_flight_boost]));
+ if (stats.has_gps) {
+ new FlightStat(layout, y++, "Pad location",
+ pos(stats.pad_lat,"N","S"),
+ pos(stats.pad_lon,"E","W"));
+ new FlightStat(layout, y++, "Last reported location",
+ pos(stats.lat,"N","S"),
+ pos(stats.lon,"E","W"));
+ }
+ }
+}
diff --git a/altosuilib/AltosFontListener.java b/altosuilib/AltosFontListener.java
index da903352..a98cc131 100644
--- a/altosuilib/AltosFontListener.java
+++ b/altosuilib/AltosFontListener.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
public interface AltosFontListener {
void font_size_changed(int font_size);
diff --git a/altosuilib/AltosFreqList.java b/altosuilib/AltosFreqList.java
new file mode 100644
index 00000000..e1299aae
--- /dev/null
+++ b/altosuilib/AltosFreqList.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosFreqList extends JComboBox<AltosFrequency> {
+
+ String product;
+ int serial;
+ int calibrate;
+
+ public void set_frequency(double new_frequency) {
+ int i;
+
+ if (new_frequency < 0) {
+ setVisible(false);
+ return;
+ }
+
+ for (i = 0; i < getItemCount(); i++) {
+ AltosFrequency f = (AltosFrequency) getItemAt(i);
+
+ if (f.close(new_frequency)) {
+ setSelectedIndex(i);
+ return;
+ }
+ }
+ for (i = 0; i < getItemCount(); i++) {
+ AltosFrequency f = (AltosFrequency) getItemAt(i);
+
+ if (new_frequency < f.frequency)
+ break;
+ }
+ String description = String.format("%s serial %d", product, serial);
+ AltosFrequency frequency = new AltosFrequency(new_frequency, description);
+ AltosUIPreferences.add_common_frequency(frequency);
+ insertItemAt(frequency, i);
+ setMaximumRowCount(getItemCount());
+ }
+
+ public void set_product(String new_product) {
+ product = new_product;
+ }
+
+ public void set_serial(int new_serial) {
+ serial = new_serial;
+ }
+
+ public double frequency() {
+ AltosFrequency f = (AltosFrequency) getSelectedItem();
+ if (f != null)
+ return f.frequency;
+ return 434.550;
+ }
+
+ public AltosFreqList () {
+ super(AltosUIPreferences.common_frequencies());
+ setMaximumRowCount(getItemCount());
+ setEditable(false);
+ product = "Unknown";
+ serial = 0;
+ }
+
+ public AltosFreqList(double in_frequency) {
+ this();
+ set_frequency(in_frequency);
+ }
+}
diff --git a/altosuilib/AltosGraph.java b/altosuilib/AltosGraph.java
new file mode 100644
index 00000000..f8c8b27b
--- /dev/null
+++ b/altosuilib/AltosGraph.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.io.*;
+import java.util.ArrayList;
+
+import java.awt.*;
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+
+import org.jfree.ui.*;
+import org.jfree.chart.*;
+import org.jfree.chart.plot.*;
+import org.jfree.chart.axis.*;
+import org.jfree.chart.renderer.*;
+import org.jfree.chart.renderer.xy.*;
+import org.jfree.chart.labels.*;
+import org.jfree.data.xy.*;
+import org.jfree.data.*;
+
+class AltosVoltage extends AltosUnits {
+
+ public double value(double v, boolean imperial_units) {
+ return v;
+ }
+
+ public double inverse(double v, boolean imperial_units) {
+ return v;
+ }
+
+ public String show_units(boolean imperial_units) {
+ return "V";
+ }
+
+ public String say_units(boolean imperial_units) {
+ return "volts";
+ }
+
+ public int show_fraction(int width, boolean imperial_units) {
+ return width / 2;
+ }
+}
+
+class AltosNsat extends AltosUnits {
+
+ public double value(double v, boolean imperial_units) {
+ return v;
+ }
+
+ public double inverse(double v, boolean imperial_units) {
+ return v;
+ }
+
+ public String show_units(boolean imperial_units) {
+ return "Sats";
+ }
+
+ public String say_units(boolean imperial_units) {
+ return "Satellites";
+ }
+
+ public int show_fraction(int width, boolean imperial_units) {
+ return 0;
+ }
+}
+
+class AltosPressure extends AltosUnits {
+
+ public double value(double p, boolean imperial_units) {
+ return p;
+ }
+
+ public double inverse(double p, boolean imperial_units) {
+ return p;
+ }
+
+ public String show_units(boolean imperial_units) {
+ return "Pa";
+ }
+
+ public String say_units(boolean imperial_units) {
+ return "pascals";
+ }
+
+ public int show_fraction(int width, boolean imperial_units) {
+ return 0;
+ }
+}
+
+class AltosDbm extends AltosUnits {
+
+ public double value(double d, boolean imperial_units) {
+ return d;
+ }
+
+ public double inverse(double d, boolean imperial_units) {
+ return d;
+ }
+
+ public String show_units(boolean imperial_units) {
+ return "dBm";
+ }
+
+ public String say_units(boolean imperial_units) {
+ return "D B M";
+ }
+
+ public int show_fraction(int width, boolean imperial_units) {
+ return 0;
+ }
+}
+
+class AltosGyroUnits extends AltosUnits {
+
+ public double value(double p, boolean imperial_units) {
+ return p;
+ }
+
+ public double inverse(double p, boolean imperial_units) {
+ return p;
+ }
+
+ public String show_units(boolean imperial_units) {
+ return "°/sec";
+ }
+
+ public String say_units(boolean imperial_units) {
+ return "degrees per second";
+ }
+
+ public int show_fraction(int width, boolean imperial_units) {
+ return 1;
+ }
+}
+
+class AltosMagUnits extends AltosUnits {
+
+ public double value(double p, boolean imperial_units) {
+ return p;
+ }
+
+ public double inverse(double p, boolean imperial_units) {
+ return p;
+ }
+
+ public String show_units(boolean imperial_units) {
+ return "Ga";
+ }
+
+ public String say_units(boolean imperial_units) {
+ return "gauss";
+ }
+
+ public int show_fraction(int width, boolean imperial_units) {
+ return 2;
+ }
+}
+
+public class AltosGraph extends AltosUIGraph {
+
+ static final private Color height_color = new Color(194,31,31);
+ static final private Color gps_height_color = new Color(150,31,31);
+ static final private Color pressure_color = new Color (225,31,31);
+ static final private Color range_color = new Color(100, 31, 31);
+ static final private Color distance_color = new Color(100, 31, 194);
+ static final private Color speed_color = new Color(31,194,31);
+ static final private Color accel_color = new Color(31,31,194);
+ static final private Color voltage_color = new Color(194, 194, 31);
+ static final private Color battery_voltage_color = new Color(194, 194, 31);
+ static final private Color drogue_voltage_color = new Color(150, 150, 31);
+ static final private Color main_voltage_color = new Color(100, 100, 31);
+ static final private Color gps_nsat_color = new Color (194, 31, 194);
+ static final private Color gps_nsat_solution_color = new Color (194, 31, 194);
+ static final private Color gps_nsat_view_color = new Color (150, 31, 150);
+ static final private Color gps_course_color = new Color (100, 31, 112);
+ static final private Color gps_ground_speed_color = new Color (31, 112, 100);
+ static final private Color gps_climb_rate_color = new Color (31, 31, 112);
+ static final private Color temperature_color = new Color (31, 194, 194);
+ static final private Color dbm_color = new Color(31, 100, 100);
+ static final private Color state_color = new Color(0,0,0);
+ static final private Color accel_x_color = new Color(255, 0, 0);
+ static final private Color accel_y_color = new Color(0, 255, 0);
+ static final private Color accel_z_color = new Color(0, 0, 255);
+ static final private Color gyro_x_color = new Color(192, 0, 0);
+ static final private Color gyro_y_color = new Color(0, 192, 0);
+ static final private Color gyro_z_color = new Color(0, 0, 192);
+ static final private Color mag_x_color = new Color(128, 0, 0);
+ static final private Color mag_y_color = new Color(0, 128, 0);
+ static final private Color mag_z_color = new Color(0, 0, 128);
+ static final private Color orient_color = new Color(31, 31, 31);
+
+ static AltosVoltage voltage_units = new AltosVoltage();
+ static AltosPressure pressure_units = new AltosPressure();
+ static AltosNsat nsat_units = new AltosNsat();
+ static AltosDbm dbm_units = new AltosDbm();
+ static AltosGyroUnits gyro_units = new AltosGyroUnits();
+ static AltosOrient orient_units = new AltosOrient();
+ static AltosMagUnits mag_units = new AltosMagUnits();
+
+ AltosUIAxis height_axis, speed_axis, accel_axis, voltage_axis, temperature_axis, nsat_axis, dbm_axis;
+ AltosUIAxis distance_axis, pressure_axis;
+ AltosUIAxis gyro_axis, orient_axis, mag_axis;
+ AltosUIAxis course_axis;
+
+ public AltosGraph(AltosUIEnable enable, AltosFlightStats stats, AltosGraphDataSet dataSet) {
+ super(enable);
+
+ height_axis = newAxis("Height", AltosConvert.height, height_color);
+ pressure_axis = newAxis("Pressure", pressure_units, pressure_color, 0);
+ speed_axis = newAxis("Speed", AltosConvert.speed, speed_color);
+ accel_axis = newAxis("Acceleration", AltosConvert.accel, accel_color);
+ voltage_axis = newAxis("Voltage", voltage_units, voltage_color);
+ temperature_axis = newAxis("Temperature", AltosConvert.temperature, temperature_color, 0);
+ nsat_axis = newAxis("Satellites", nsat_units, gps_nsat_color,
+ AltosUIAxis.axis_include_zero | AltosUIAxis.axis_integer);
+ dbm_axis = newAxis("Signal Strength", dbm_units, dbm_color, 0);
+ distance_axis = newAxis("Distance", AltosConvert.distance, range_color);
+
+ gyro_axis = newAxis("Rotation Rate", gyro_units, gyro_z_color, 0);
+ orient_axis = newAxis("Tilt Angle", orient_units, orient_color, 0);
+ mag_axis = newAxis("Magnetic Field", mag_units, mag_x_color, 0);
+ course_axis = newAxis("Course", orient_units, gps_course_color, 0);
+
+ addMarker("State", AltosGraphDataPoint.data_state, state_color);
+
+ if (stats.has_flight_data) {
+ addSeries("Height",
+ AltosGraphDataPoint.data_height,
+ AltosConvert.height,
+ height_color,
+ true,
+ height_axis);
+ addSeries("Pressure",
+ AltosGraphDataPoint.data_pressure,
+ pressure_units,
+ pressure_color,
+ false,
+ pressure_axis);
+ addSeries("Speed",
+ AltosGraphDataPoint.data_speed,
+ AltosConvert.speed,
+ speed_color,
+ true,
+ speed_axis);
+ addSeries("Acceleration",
+ AltosGraphDataPoint.data_accel,
+ AltosConvert.accel,
+ accel_color,
+ true,
+ accel_axis);
+ }
+ if (stats.has_gps) {
+ boolean enable_gps = false;
+
+ if (!stats.has_flight_data)
+ enable_gps = true;
+
+ addSeries("Range",
+ AltosGraphDataPoint.data_range,
+ AltosConvert.distance,
+ range_color,
+ false,
+ distance_axis);
+ addSeries("Distance",
+ AltosGraphDataPoint.data_distance,
+ AltosConvert.distance,
+ distance_color,
+ enable_gps,
+ distance_axis);
+ addSeries("GPS Height",
+ AltosGraphDataPoint.data_gps_height,
+ AltosConvert.height,
+ gps_height_color,
+ enable_gps,
+ height_axis);
+ addSeries("GPS Altitude",
+ AltosGraphDataPoint.data_gps_altitude,
+ AltosConvert.height,
+ gps_height_color,
+ false,
+ height_axis);
+ addSeries("GPS Satellites in Solution",
+ AltosGraphDataPoint.data_gps_nsat_solution,
+ nsat_units,
+ gps_nsat_solution_color,
+ false,
+ nsat_axis);
+ addSeries("GPS Satellites in View",
+ AltosGraphDataPoint.data_gps_nsat_view,
+ nsat_units,
+ gps_nsat_view_color,
+ false,
+ nsat_axis);
+ addSeries("GPS Course",
+ AltosGraphDataPoint.data_gps_course,
+ orient_units,
+ gps_course_color,
+ false,
+ course_axis);
+ addSeries("GPS Ground Speed",
+ AltosGraphDataPoint.data_gps_ground_speed,
+ AltosConvert.speed,
+ gps_ground_speed_color,
+ enable_gps,
+ speed_axis);
+ addSeries("GPS Climb Rate",
+ AltosGraphDataPoint.data_gps_climb_rate,
+ AltosConvert.speed,
+ gps_climb_rate_color,
+ enable_gps,
+ speed_axis);
+ }
+ if (stats.has_rssi)
+ addSeries("Received Signal Strength",
+ AltosGraphDataPoint.data_rssi,
+ dbm_units,
+ dbm_color,
+ false,
+ dbm_axis);
+
+ if (stats.has_battery)
+ addSeries("Battery Voltage",
+ AltosGraphDataPoint.data_battery_voltage,
+ voltage_units,
+ battery_voltage_color,
+ false,
+ voltage_axis);
+
+ if (stats.has_flight_adc) {
+ addSeries("Temperature",
+ AltosGraphDataPoint.data_temperature,
+ AltosConvert.temperature,
+ temperature_color,
+ false,
+ temperature_axis);
+ addSeries("Drogue Voltage",
+ AltosGraphDataPoint.data_drogue_voltage,
+ voltage_units,
+ drogue_voltage_color,
+ false,
+ voltage_axis);
+ addSeries("Main Voltage",
+ AltosGraphDataPoint.data_main_voltage,
+ voltage_units,
+ main_voltage_color,
+ false,
+ voltage_axis);
+ }
+
+ if (stats.has_imu) {
+ addSeries("Acceleration X",
+ AltosGraphDataPoint.data_accel_x,
+ AltosConvert.accel,
+ accel_x_color,
+ false,
+ accel_axis);
+ addSeries("Acceleration Y",
+ AltosGraphDataPoint.data_accel_y,
+ AltosConvert.accel,
+ accel_y_color,
+ false,
+ accel_axis);
+ addSeries("Acceleration Z",
+ AltosGraphDataPoint.data_accel_z,
+ AltosConvert.accel,
+ accel_z_color,
+ false,
+ accel_axis);
+ addSeries("Rotation Rate X",
+ AltosGraphDataPoint.data_gyro_x,
+ gyro_units,
+ gyro_x_color,
+ false,
+ gyro_axis);
+ addSeries("Rotation Rate Y",
+ AltosGraphDataPoint.data_gyro_y,
+ gyro_units,
+ gyro_y_color,
+ false,
+ gyro_axis);
+ addSeries("Rotation Rate Z",
+ AltosGraphDataPoint.data_gyro_z,
+ gyro_units,
+ gyro_z_color,
+ false,
+ gyro_axis);
+ }
+ if (stats.has_mag) {
+ addSeries("Magnetometer X",
+ AltosGraphDataPoint.data_mag_x,
+ mag_units,
+ mag_x_color,
+ false,
+ mag_axis);
+ addSeries("Magnetometer Y",
+ AltosGraphDataPoint.data_mag_y,
+ mag_units,
+ mag_y_color,
+ false,
+ mag_axis);
+ addSeries("Magnetometer Z",
+ AltosGraphDataPoint.data_mag_z,
+ mag_units,
+ mag_z_color,
+ false,
+ mag_axis);
+ }
+ if (stats.has_orient)
+ addSeries("Tilt Angle",
+ AltosGraphDataPoint.data_orient,
+ orient_units,
+ orient_color,
+ false,
+ orient_axis);
+ if (stats.num_ignitor > 0) {
+ for (int i = 0; i < stats.num_ignitor; i++)
+ addSeries(AltosLib.ignitor_name(i),
+ AltosGraphDataPoint.data_ignitor_0 + i,
+ voltage_units,
+ main_voltage_color,
+ false,
+ voltage_axis);
+ for (int i = 0; i < stats.num_ignitor; i++)
+ addMarker(AltosLib.ignitor_name(i), AltosGraphDataPoint.data_ignitor_fired_0 + i, state_color);
+ }
+
+ setDataSet(dataSet);
+ }
+}
diff --git a/altosuilib/AltosGraphDataPoint.java b/altosuilib/AltosGraphDataPoint.java
new file mode 100644
index 00000000..3aff1e82
--- /dev/null
+++ b/altosuilib/AltosGraphDataPoint.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosGraphDataPoint implements AltosUIDataPoint {
+
+ AltosState state;
+
+ public static final int data_height = 0;
+ public static final int data_speed = 1;
+ public static final int data_accel = 2;
+ public static final int data_temp = 3;
+ public static final int data_battery_voltage = 4;
+ public static final int data_drogue_voltage = 5;
+ public static final int data_main_voltage = 6;
+ public static final int data_rssi = 7;
+ public static final int data_state = 8;
+ public static final int data_gps_height = 9;
+ public static final int data_gps_nsat_solution = 10;
+ public static final int data_gps_nsat_view = 11;
+ public static final int data_gps_altitude = 12;
+ public static final int data_temperature = 13;
+ public static final int data_range = 14;
+ public static final int data_distance = 15;
+ public static final int data_pressure = 16;
+ public static final int data_accel_x = 17;
+ public static final int data_accel_y = 18;
+ public static final int data_accel_z = 19;
+ public static final int data_gyro_x = 20;
+ public static final int data_gyro_y = 21;
+ public static final int data_gyro_z = 22;
+ public static final int data_mag_x = 23;
+ public static final int data_mag_y = 24;
+ public static final int data_mag_z = 25;
+ public static final int data_orient = 26;
+ public static final int data_gps_course = 27;
+ public static final int data_gps_ground_speed = 28;
+ public static final int data_gps_climb_rate = 29;
+ public static final int data_ignitor_0 = 30;
+ public static final int data_ignitor_num = 32;
+ public static final int data_ignitor_max = data_ignitor_0 + data_ignitor_num - 1;
+ public static final int data_ignitor_fired_0 = data_ignitor_0 + data_ignitor_num;
+ public static final int data_ignitor_fired_max = data_ignitor_fired_0 + data_ignitor_num - 1;
+
+ public double x() throws AltosUIDataMissing {
+ double time = state.time_since_boost();
+ if (time < -2)
+ throw new AltosUIDataMissing(-1);
+ return time;
+ }
+
+ public double y(int index) throws AltosUIDataMissing {
+ double y = AltosLib.MISSING;
+ switch (index) {
+ case data_height:
+ y = state.height();
+ break;
+ case data_speed:
+ y = state.speed();
+ break;
+ case data_accel:
+ y = state.acceleration();
+ break;
+ case data_temp:
+ y = state.temperature;
+ break;
+ case data_battery_voltage:
+ y = state.battery_voltage;
+ break;
+ case data_drogue_voltage:
+ y = state.apogee_voltage;
+ break;
+ case data_main_voltage:
+ y = state.main_voltage;
+ break;
+ case data_rssi:
+ y = state.rssi;
+ break;
+ case data_gps_height:
+ y = state.gps_height;
+ break;
+ case data_gps_nsat_solution:
+ if (state.gps != null)
+ y = state.gps.nsat;
+ break;
+ case data_gps_nsat_view:
+ if (state.gps != null) {
+ if (state.gps.cc_gps_sat != null)
+ y = state.gps.cc_gps_sat.length;
+ else
+ y = 0;
+ }
+ break;
+ case data_gps_altitude:
+ y = state.gps_altitude();
+ break;
+ case data_temperature:
+ y = state.temperature;
+ break;
+ case data_range:
+ y = state.range;
+ break;
+ case data_distance:
+ if (state.from_pad != null)
+ y = state.from_pad.distance;
+ break;
+ case data_pressure:
+ y = state.pressure();
+ break;
+
+ case data_accel_x:
+ case data_accel_y:
+ case data_accel_z:
+ case data_gyro_x:
+ case data_gyro_y:
+ case data_gyro_z:
+ AltosIMU imu = state.imu;
+ if (imu == null)
+ break;
+ switch (index) {
+ case data_accel_x:
+ y = imu.accel_x;
+ break;
+ case data_accel_y:
+ y = imu.accel_y;
+ break;
+ case data_accel_z:
+ y = imu.accel_z;
+ break;
+ case data_gyro_x:
+ y = imu.gyro_x;
+ break;
+ case data_gyro_y:
+ y = imu.gyro_y;
+ break;
+ case data_gyro_z:
+ y = imu.gyro_z;
+ break;
+ }
+ break;
+ case data_mag_x:
+ case data_mag_y:
+ case data_mag_z:
+ AltosMag mag = state.mag;
+ if (mag == null)
+ break;
+ switch (index) {
+ case data_mag_x:
+ y = mag.x;
+ break;
+ case data_mag_y:
+ y = mag.y;
+ break;
+ case data_mag_z:
+ y = mag.z;
+ break;
+ }
+ break;
+ case data_orient:
+ y = state.orient();
+ break;
+ case data_gps_course:
+ if (state.gps != null)
+ y = state.gps.course;
+ else
+ y = AltosLib.MISSING;
+ break;
+ case data_gps_ground_speed:
+ if (state.gps != null)
+ y = state.gps.ground_speed;
+ else
+ y = AltosLib.MISSING;
+ break;
+ case data_gps_climb_rate:
+ if (state.gps != null)
+ y = state.gps.climb_rate;
+ else
+ y = AltosLib.MISSING;
+ break;
+ default:
+ if (data_ignitor_0 <= index && index <= data_ignitor_max) {
+ int ignitor = index - data_ignitor_0;
+ if (state.ignitor_voltage != null && ignitor < state.ignitor_voltage.length)
+ y = state.ignitor_voltage[ignitor];
+ } else if (data_ignitor_fired_0 <= index && index <= data_ignitor_fired_max) {
+ int ignitor = index - data_ignitor_fired_0;
+ if (state.ignitor_voltage != null && ignitor < state.ignitor_voltage.length) {
+ if ((state.pyro_fired & (1 << ignitor)) != 0)
+ y = 1;
+ else
+ y = 0;
+ }
+ }
+ break;
+ }
+ if (y == AltosLib.MISSING)
+ throw new AltosUIDataMissing(index);
+ return y;
+ }
+
+ public int id(int index) {
+ if (index == data_state) {
+ int s = state.state;
+ if (AltosLib.ao_flight_boost <= s && s <= AltosLib.ao_flight_landed)
+ return s;
+ } else if (data_ignitor_fired_0 <= index && index <= data_ignitor_fired_max) {
+ int ignitor = index - data_ignitor_fired_0;
+ if (state.ignitor_voltage != null && ignitor < state.ignitor_voltage.length) {
+ if (state.ignitor_voltage != null && ignitor < state.ignitor_voltage.length) {
+ if ((state.pyro_fired & (1 << ignitor)) != 0)
+ return 1;
+ }
+ }
+ }
+ return -1;
+ }
+
+ public String id_name(int index) {
+ if (index == data_state) {
+ return state.state_name();
+ } else if (data_ignitor_fired_0 <= index && index <= data_ignitor_fired_max) {
+ int ignitor = index - data_ignitor_fired_0;
+ if (state.ignitor_voltage != null && ignitor < state.ignitor_voltage.length)
+ return AltosLib.ignitor_name(ignitor);
+ }
+ return "";
+ }
+
+ public AltosGraphDataPoint (AltosState state) {
+ this.state = state;
+ }
+}
diff --git a/altosuilib/AltosGraphDataSet.java b/altosuilib/AltosGraphDataSet.java
new file mode 100644
index 00000000..36933e9b
--- /dev/null
+++ b/altosuilib/AltosGraphDataSet.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.lang.*;
+import java.io.*;
+import java.util.*;
+import org.altusmetrum.altoslib_4.*;
+
+class AltosGraphIterator implements Iterator<AltosUIDataPoint> {
+ AltosGraphDataSet dataSet;
+ Iterator<AltosState> iterator;
+
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ public AltosUIDataPoint next() {
+ AltosState state = iterator.next();
+
+ if (state.flight != AltosLib.MISSING) {
+ if (dataSet.callsign == null && state.callsign != null)
+ dataSet.callsign = state.callsign;
+
+ if (dataSet.serial == 0 && state.serial != 0)
+ dataSet.serial = state.serial;
+
+ if (dataSet.flight == 0 && state.flight != 0)
+ dataSet.flight = state.flight;
+ }
+
+ return new AltosGraphDataPoint(state);
+ }
+
+ public AltosGraphIterator (Iterator<AltosState> iterator, AltosGraphDataSet dataSet) {
+ this.iterator = iterator;
+ this.dataSet = dataSet;
+ }
+
+ public void remove() {
+ }
+}
+
+class AltosGraphIterable implements Iterable<AltosUIDataPoint> {
+ AltosGraphDataSet dataSet;
+
+ public Iterator<AltosUIDataPoint> iterator() {
+ return new AltosGraphIterator(dataSet.states.iterator(), dataSet);
+ }
+
+ public AltosGraphIterable(AltosGraphDataSet dataSet) {
+ this.dataSet = dataSet;
+ }
+}
+
+public class AltosGraphDataSet implements AltosUIDataSet {
+ String callsign;
+ int serial;
+ int flight;
+ AltosStateIterable states;
+
+ public String name() {
+ if (callsign != null)
+ return String.format("%s - %d/%d", callsign, serial, flight);
+ else
+ return String.format("%d/%d", serial, flight);
+ }
+
+ public Iterable<AltosUIDataPoint> dataPoints() {
+ return new AltosGraphIterable(this);
+ }
+
+ public AltosGraphDataSet (AltosStateIterable states) {
+ this.states = states;
+ this.callsign = null;
+ this.serial = 0;
+ this.flight = 0;
+ }
+}
diff --git a/altosuilib/AltosInfoTable.java b/altosuilib/AltosInfoTable.java
new file mode 100644
index 00000000..23ae4ae5
--- /dev/null
+++ b/altosuilib/AltosInfoTable.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.table.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosInfoTable extends JTable implements AltosFlightDisplay, HierarchyListener {
+ private AltosFlightInfoTableModel model;
+
+ static final int info_columns = 3;
+ static final int info_rows = 17;
+
+ private AltosState last_state;
+ private AltosListenerState last_listener_state;
+
+ int desired_row_height() {
+ FontMetrics infoValueMetrics = getFontMetrics(AltosUILib.table_value_font);
+ return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 18 / 10;
+ }
+
+ int text_width(String t) {
+ FontMetrics infoValueMetrics = getFontMetrics(AltosUILib.table_value_font);
+
+ return infoValueMetrics.stringWidth(t);
+ }
+
+ void set_layout() {
+ setRowHeight(desired_row_height());
+ for (int i = 0; i < info_columns * 2; i++)
+ {
+ TableColumn column = getColumnModel().getColumn(i);
+
+ if ((i & 1) == 0)
+ column.setPreferredWidth(text_width(" Satellites Visible"));
+ else
+ column.setPreferredWidth(text_width("W 179°59.99999' "));
+ }
+ }
+
+ public AltosInfoTable() {
+ super(new AltosFlightInfoTableModel(info_rows, info_columns));
+ model = (AltosFlightInfoTableModel) getModel();
+ setFont(AltosUILib.table_value_font);
+ addHierarchyListener(this);
+ setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS);
+ setShowGrid(true);
+ set_layout();
+ doLayout();
+ }
+
+ public void font_size_changed(int font_size) {
+ setFont(AltosUILib.table_value_font);
+ set_layout();
+ doLayout();
+ }
+
+ public void units_changed(boolean imperial_units) {
+ }
+
+ public void hierarchyChanged(HierarchyEvent e) {
+ if (last_state != null && isShowing()) {
+ AltosState state = last_state;
+ AltosListenerState listener_state = last_listener_state;
+
+ last_state = null;
+ last_listener_state = null;
+ show(state, listener_state);
+ }
+ }
+
+ public Dimension getPreferredScrollableViewportSize() {
+ return getPreferredSize();
+ }
+
+ public void reset() {
+ model.reset();
+ }
+
+ void info_add_row(int col, String name, String value) {
+ model.addRow(col, name, value);
+ }
+
+ void info_add_row(int col, String name, String format, Object... parameters) {
+ info_add_row (col, name, String.format(format, parameters));
+ }
+
+ void info_add_deg(int col, String name, double v, int pos, int neg) {
+ int c = pos;
+ if (v < 0) {
+ c = neg;
+ v = -v;
+ }
+ double deg = Math.floor(v);
+ double min = (v - deg) * 60;
+
+ info_add_row(col, name, String.format("%c %3.0f°%08.5f'", c, deg, min));
+ }
+
+ void info_finish() {
+ model.finish();
+ }
+
+ public void clear() {
+ model.clear();
+ }
+
+ public String getName() { return "Table"; }
+
+ public void show(AltosState state, AltosListenerState listener_state) {
+
+ if (!isShowing()) {
+ last_state = state;
+ last_listener_state = listener_state;
+ return;
+ }
+
+ reset();
+ if (state != null) {
+ if (state.device_type != AltosLib.MISSING)
+ info_add_row(0, "Device", "%s", AltosLib.product_name(state.device_type));
+ if (state.altitude() != AltosLib.MISSING)
+ info_add_row(0, "Altitude", "%6.0f m", state.altitude());
+ if (state.ground_altitude() != AltosLib.MISSING)
+ info_add_row(0, "Pad altitude", "%6.0f m", state.ground_altitude());
+ if (state.height() != AltosLib.MISSING)
+ info_add_row(0, "Height", "%6.0f m", state.height());
+ if (state.max_height() != AltosLib.MISSING)
+ info_add_row(0, "Max height", "%6.0f m", state.max_height());
+ if (state.acceleration() != AltosLib.MISSING)
+ info_add_row(0, "Acceleration", "%8.1f m/s²", state.acceleration());
+ if (state.max_acceleration() != AltosLib.MISSING)
+ info_add_row(0, "Max acceleration", "%8.1f m/s²", state.max_acceleration());
+ if (state.speed() != AltosLib.MISSING)
+ info_add_row(0, "Speed", "%8.1f m/s", state.speed());
+ if (state.max_speed() != AltosLib.MISSING)
+ info_add_row(0, "Max Speed", "%8.1f m/s", state.max_speed());
+ if (state.orient() != AltosLib.MISSING)
+ info_add_row(0, "Tilt", "%4.0f °", state.orient());
+ if (state.max_orient() != AltosLib.MISSING)
+ info_add_row(0, "Max Tilt", "%4.0f °", state.max_orient());
+ if (state.temperature != AltosLib.MISSING)
+ info_add_row(0, "Temperature", "%9.2f °C", state.temperature);
+ if (state.battery_voltage != AltosLib.MISSING)
+ info_add_row(0, "Battery", "%9.2f V", state.battery_voltage);
+ if (state.apogee_voltage != AltosLib.MISSING)
+ info_add_row(0, "Drogue", "%9.2f V", state.apogee_voltage);
+ if (state.main_voltage != AltosLib.MISSING)
+ info_add_row(0, "Main", "%9.2f V", state.main_voltage);
+ }
+ if (listener_state != null) {
+ info_add_row(0, "CRC Errors", "%6d", listener_state.crc_errors);
+
+ if (listener_state.battery != AltosLib.MISSING)
+ info_add_row(0, "Receiver Battery", "%9.2f", listener_state.battery);
+ }
+
+ if (state != null) {
+ if (state.gps == null || !state.gps.connected) {
+ info_add_row(1, "GPS", "not available");
+ } else {
+ if (state.gps_ready)
+ info_add_row(1, "GPS state", "%s", "ready");
+ else
+ info_add_row(1, "GPS state", "wait (%d)",
+ state.gps_waiting);
+ if (state.gps.locked)
+ info_add_row(1, "GPS", " locked");
+ else if (state.gps.connected)
+ info_add_row(1, "GPS", " unlocked");
+ else
+ info_add_row(1, "GPS", " missing");
+ info_add_row(1, "Satellites", "%6d", state.gps.nsat);
+ if (state.gps.lat != AltosLib.MISSING)
+ info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S');
+ if (state.gps.lon != AltosLib.MISSING)
+ info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W');
+ if (state.gps.alt != AltosLib.MISSING)
+ info_add_row(1, "GPS altitude", "%8.1f", state.gps.alt);
+ if (state.gps_height != AltosLib.MISSING)
+ info_add_row(1, "GPS height", "%8.1f", state.gps_height);
+
+ /* The SkyTraq GPS doesn't report these values */
+ /*
+ if (false) {
+ info_add_row(1, "GPS ground speed", "%8.1f m/s %3d°",
+ state.gps.ground_speed,
+ state.gps.course);
+ info_add_row(1, "GPS climb rate", "%8.1f m/s",
+ state.gps.climb_rate);
+ info_add_row(1, "GPS error", "%6d m(h)%3d m(v)",
+ state.gps.h_error, state.gps.v_error);
+ }
+ */
+
+ info_add_row(1, "GPS hdop", "%8.1f", state.gps.hdop);
+
+ if (state.npad > 0) {
+ if (state.from_pad != null) {
+ info_add_row(1, "Distance from pad", "%6d m",
+ (int) (state.from_pad.distance + 0.5));
+ info_add_row(1, "Direction from pad", "%6d°",
+ (int) (state.from_pad.bearing + 0.5));
+ info_add_row(1, "Elevation from pad", "%6d°",
+ (int) (state.elevation + 0.5));
+ info_add_row(1, "Range from pad", "%6d m",
+ (int) (state.range + 0.5));
+ } else {
+ info_add_row(1, "Distance from pad", "unknown");
+ info_add_row(1, "Direction from pad", "unknown");
+ info_add_row(1, "Elevation from pad", "unknown");
+ info_add_row(1, "Range from pad", "unknown");
+ }
+ info_add_deg(1, "Pad latitude", state.pad_lat, 'N', 'S');
+ info_add_deg(1, "Pad longitude", state.pad_lon, 'E', 'W');
+ info_add_row(1, "Pad GPS alt", "%6.0f m", state.pad_alt);
+ }
+ if (state.gps.year != AltosLib.MISSING)
+ info_add_row(1, "GPS date", "%04d-%02d-%02d",
+ state.gps.year,
+ state.gps.month,
+ state.gps.day);
+ if (state.gps.hour != AltosLib.MISSING)
+ info_add_row(1, "GPS time", " %02d:%02d:%02d",
+ state.gps.hour,
+ state.gps.minute,
+ state.gps.second);
+ //int nsat_vis = 0;
+ int c;
+
+ if (state.gps.cc_gps_sat == null)
+ info_add_row(2, "Satellites Visible", "%4d", 0);
+ else {
+ info_add_row(2, "Satellites Visible", "%4d", state.gps.cc_gps_sat.length);
+ for (c = 0; c < state.gps.cc_gps_sat.length; c++) {
+ info_add_row(2, "Satellite id,C/N0",
+ "%4d, %4d",
+ state.gps.cc_gps_sat[c].svid,
+ state.gps.cc_gps_sat[c].c_n0);
+ }
+ }
+ }
+ }
+ info_finish();
+ }
+}
diff --git a/altosuilib/AltosLed.java b/altosuilib/AltosLed.java
new file mode 100644
index 00000000..2debb62a
--- /dev/null
+++ b/altosuilib/AltosLed.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import javax.swing.*;
+
+public class AltosLed extends JLabel {
+ ImageIcon on, off;
+
+ ImageIcon create_icon(String path) {
+ java.net.URL imgURL = AltosUILib.class.getResource(path);
+ if (imgURL != null)
+ return new ImageIcon(imgURL);
+ System.err.printf("Cannot find icon \"%s\"\n", path);
+ return null;
+ }
+
+ public void set(boolean set) {
+ if (set)
+ setIcon(on);
+ else
+ setIcon(off);
+ }
+
+ public AltosLed(String on_path, String off_path) {
+ on = create_icon(on_path);
+ off = create_icon(off_path);
+ setIcon(off);
+ }
+}
diff --git a/altosuilib/AltosLights.java b/altosuilib/AltosLights.java
new file mode 100644
index 00000000..c91b70e9
--- /dev/null
+++ b/altosuilib/AltosLights.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import javax.swing.*;
+
+public class AltosLights extends JComponent {
+
+ GridBagLayout gridbag;
+
+ AltosLed red, green;
+
+ ImageIcon create_icon(String path, String description) {
+ java.net.URL imgURL = AltosUILib.class.getResource(path);
+ if (imgURL != null)
+ return new ImageIcon(imgURL, description);
+ System.err.printf("Cannot find icon \"%s\"\n", path);
+ return null;
+ }
+
+ public void set (boolean on) {
+ if (on) {
+ red.set(false);
+ green.set(true);
+ } else {
+ red.set(true);
+ green.set(false);
+ }
+ }
+
+ public AltosLights() {
+ GridBagConstraints c;
+ gridbag = new GridBagLayout();
+ setLayout(gridbag);
+
+ c = new GridBagConstraints();
+ red = new AltosLed("/redled.png", "/grayled.png");
+ c.gridx = 0; c.gridy = 0;
+ c.insets = new Insets (0, 5, 0, 5);
+ gridbag.setConstraints(red, c);
+ add(red);
+ red.set(true);
+ green = new AltosLed("/greenled.png", "/grayled.png");
+ c.gridx = 1; c.gridy = 0;
+ gridbag.setConstraints(green, c);
+ add(green);
+ green.set(false);
+ }
+}
diff --git a/altosuilib/AltosPositionListener.java b/altosuilib/AltosPositionListener.java
index e75d2de5..34cf1650 100644
--- a/altosuilib/AltosPositionListener.java
+++ b/altosuilib/AltosPositionListener.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
public interface AltosPositionListener {
public void position_changed(int position);
diff --git a/altosuilib/AltosRomconfigUI.java b/altosuilib/AltosRomconfigUI.java
new file mode 100644
index 00000000..8f002c4a
--- /dev/null
+++ b/altosuilib/AltosRomconfigUI.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosRomconfigUI
+ extends AltosUIDialog
+ implements ActionListener
+{
+ Container pane;
+ Box box;
+ JLabel serial_label;
+ JLabel radio_calibration_label;
+
+ JFrame owner;
+ JTextField serial_value;
+ JTextField radio_calibration_value;
+
+ JButton ok;
+ JButton cancel;
+
+ /* Build the UI using a grid bag */
+ public AltosRomconfigUI(JFrame in_owner) {
+ super (in_owner, "Configure TeleMetrum Rom Values", true);
+
+ owner = in_owner;
+ GridBagConstraints c;
+
+ Insets il = new Insets(4,4,4,4);
+ Insets ir = new Insets(4,4,4,4);
+
+ pane = getContentPane();
+ pane.setLayout(new GridBagLayout());
+
+ /* Serial */
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = 0;
+ c.gridwidth = 3;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ serial_label = new JLabel("Serial:");
+ pane.add(serial_label, c);
+
+ c = new GridBagConstraints();
+ c.gridx = 3; c.gridy = 0;
+ c.gridwidth = 3;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ serial_value = new JTextField("00000000");
+ pane.add(serial_value, c);
+
+ /* Radio calibration value */
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = 1;
+ c.gridwidth = 3;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = il;
+ c.ipady = 5;
+ radio_calibration_label = new JLabel("Radio Calibration:");
+ pane.add(radio_calibration_label, c);
+
+ c = new GridBagConstraints();
+ c.gridx = 3; c.gridy = 1;
+ c.gridwidth = 3;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.weightx = 1;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.insets = ir;
+ c.ipady = 5;
+ radio_calibration_value = new JTextField("00000000");
+ pane.add(radio_calibration_value, c);
+
+ /* Buttons */
+ c = new GridBagConstraints();
+ c.gridx = 0; c.gridy = 2;
+ c.gridwidth = 3;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = il;
+ ok = new JButton("OK");
+ pane.add(ok, c);
+ ok.addActionListener(this);
+ ok.setActionCommand("ok");
+
+ c = new GridBagConstraints();
+ c.gridx = 3; c.gridy = 2;
+ c.gridwidth = 3;
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = il;
+ cancel = new JButton("Cancel");
+ pane.add(cancel, c);
+ cancel.addActionListener(this);
+ cancel.setActionCommand("cancel");
+
+ pack();
+ setLocationRelativeTo(owner);
+ }
+
+ public AltosRomconfigUI(JFrame frame, AltosRomconfig config) {
+ this(frame);
+ set(config);
+ }
+
+ boolean selected;
+
+ /* Listen for events from our buttons */
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+
+ if (cmd.equals("ok")) {
+ AltosRomconfig romconfig = romconfig();
+ if (romconfig == null || !romconfig.valid()) {
+ JOptionPane.showMessageDialog(this,
+ "Invalid serial number or radio calibration value",
+ "Invalid rom configuration",
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ selected = true;
+ }
+ setVisible(false);
+ }
+
+ int serial() {
+ return Integer.parseInt(serial_value.getText());
+ }
+
+ void set_serial(int serial) {
+ serial_value.setText(String.format("%d", serial));
+ }
+
+ int radio_calibration() {
+ return Integer.parseInt(radio_calibration_value.getText());
+ }
+
+ void set_radio_calibration(int calibration) {
+ radio_calibration_value.setText(String.format("%d", calibration));
+ }
+
+ public void set(AltosRomconfig config) {
+ if (config != null && config.valid()) {
+ set_serial(config.serial_number);
+ set_radio_calibration(config.radio_calibration);
+ }
+ }
+
+ AltosRomconfig romconfig() {
+ try {
+ return new AltosRomconfig(serial(), radio_calibration());
+ } catch (NumberFormatException ne) {
+ return null;
+ }
+ }
+
+ public AltosRomconfig showDialog() {
+ setVisible(true);
+ if (selected)
+ return romconfig();
+ return null;
+ }
+
+ public static AltosRomconfig show(JFrame frame, AltosRomconfig config) {
+ AltosRomconfigUI ui = new AltosRomconfigUI(frame, config);
+ return ui.showDialog();
+ }
+}
diff --git a/altosuilib/AltosScanUI.java b/altosuilib/AltosScanUI.java
new file mode 100644
index 00000000..b0cde059
--- /dev/null
+++ b/altosuilib/AltosScanUI.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+class AltosScanResult {
+ String callsign;
+ int serial;
+ int flight;
+ AltosFrequency frequency;
+ int telemetry;
+
+ boolean interrupted = false;
+
+ public String toString() {
+ return String.format("%-9.9s serial %-4d flight %-4d (%s %s)",
+ callsign, serial, flight, frequency.toShortString(), AltosLib.telemetry_name(telemetry));
+ }
+
+ public String toShortString() {
+ return String.format("%s %d %d %7.3f %d",
+ callsign, serial, flight, frequency, telemetry);
+ }
+
+ public AltosScanResult(String in_callsign, int in_serial,
+ int in_flight, AltosFrequency in_frequency, int in_telemetry) {
+ callsign = in_callsign;
+ serial = in_serial;
+ flight = in_flight;
+ frequency = in_frequency;
+ telemetry = in_telemetry;
+ }
+
+ public boolean equals(AltosScanResult other) {
+ return (serial == other.serial &&
+ frequency.frequency == other.frequency.frequency &&
+ telemetry == other.telemetry);
+ }
+
+ public boolean up_to_date(AltosScanResult other) {
+ if (flight == 0 && other.flight != 0) {
+ flight = other.flight;
+ return false;
+ }
+ if (callsign.equals("N0CALL") && !other.callsign.equals("N0CALL")) {
+ callsign = other.callsign;
+ return false;
+ }
+ return true;
+ }
+}
+
+class AltosScanResults extends LinkedList<AltosScanResult> implements ListModel<AltosScanResult> {
+
+ LinkedList<ListDataListener> listeners = new LinkedList<ListDataListener>();
+
+ void changed(ListDataEvent de) {
+ for (ListDataListener l : listeners)
+ l.contentsChanged(de);
+ }
+
+ public boolean add(AltosScanResult r) {
+ int i = 0;
+ for (AltosScanResult old : this) {
+ if (old.equals(r)) {
+ if (!old.up_to_date(r))
+ changed (new ListDataEvent(this,
+ ListDataEvent.CONTENTS_CHANGED,
+ i, i));
+ return true;
+ }
+ i++;
+ }
+
+ super.add(r);
+ changed(new ListDataEvent(this,
+ ListDataEvent.INTERVAL_ADDED,
+ this.size() - 2, this.size() - 1));
+ return true;
+ }
+
+ public void addListDataListener(ListDataListener l) {
+ listeners.add(l);
+ }
+
+ public void removeListDataListener(ListDataListener l) {
+ listeners.remove(l);
+ }
+
+ public AltosScanResult getElementAt(int i) {
+ return this.get(i);
+ }
+
+ public int getSize() {
+ return this.size();
+ }
+}
+
+public class AltosScanUI
+ extends AltosUIDialog
+ implements ActionListener
+{
+ AltosUIFrame owner;
+ AltosDevice device;
+ AltosConfigData config_data;
+ AltosTelemetryReader reader;
+ private JList<AltosScanResult> list;
+ private JLabel scanning_label;
+ private JLabel frequency_label;
+ private JLabel telemetry_label;
+ private JButton cancel_button;
+ private JButton monitor_button;
+ private JCheckBox[] telemetry_boxes;
+ javax.swing.Timer timer;
+ AltosScanResults results = new AltosScanResults();
+
+ int telemetry;
+ boolean select_telemetry = false;
+
+ final static int timeout = 1200;
+ TelemetryHandler handler;
+ Thread thread;
+ AltosFrequency[] frequencies;
+ int frequency_index;
+
+ void scan_exception(Exception e) {
+ if (e instanceof FileNotFoundException) {
+ JOptionPane.showMessageDialog(owner,
+ ((FileNotFoundException) e).getMessage(),
+ "Cannot open target device",
+ JOptionPane.ERROR_MESSAGE);
+ } else if (e instanceof AltosSerialInUseException) {
+ JOptionPane.showMessageDialog(owner,
+ String.format("Device \"%s\" already in use",
+ device.toShortString()),
+ "Device in use",
+ JOptionPane.ERROR_MESSAGE);
+ } else if (e instanceof IOException) {
+ IOException ee = (IOException) e;
+ JOptionPane.showMessageDialog(owner,
+ device.toShortString(),
+ ee.getLocalizedMessage(),
+ JOptionPane.ERROR_MESSAGE);
+ } else {
+ JOptionPane.showMessageDialog(owner,
+ String.format("Connection to \"%s\" failed",
+ device.toShortString()),
+ "Connection Failed",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ close();
+ }
+
+ class TelemetryHandler implements Runnable {
+
+ public void run() {
+
+ boolean interrupted = false;
+
+ try {
+ for (;;) {
+ try {
+ AltosState state = reader.read();
+ if (state == null)
+ continue;
+ if (state.flight != AltosLib.MISSING) {
+ final AltosScanResult result = new AltosScanResult(state.callsign,
+ state.serial,
+ state.flight,
+ frequencies[frequency_index],
+ telemetry);
+ Runnable r = new Runnable() {
+ public void run() {
+ results.add(result);
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+ } catch (ParseException pp) {
+ } catch (AltosCRCException ce) {
+ }
+ }
+ } catch (InterruptedException ee) {
+ interrupted = true;
+ } catch (IOException ie) {
+ } finally {
+ reader.close(interrupted);
+ }
+ }
+ }
+
+ void set_label() {
+ frequency_label.setText(String.format("Frequency: %s", frequencies[frequency_index].toString()));
+ if (select_telemetry)
+ telemetry_label.setText(String.format("Telemetry: %s", AltosLib.telemetry_name(telemetry)));
+ }
+
+ void set_telemetry() {
+ reader.set_telemetry(telemetry);
+ }
+
+ void set_frequency() throws InterruptedException, TimeoutException {
+ reader.set_frequency(frequencies[frequency_index].frequency);
+ reader.reset();
+ }
+
+ void next() throws InterruptedException, TimeoutException {
+ reader.set_monitor(false);
+ Thread.sleep(100);
+ ++frequency_index;
+ if (select_telemetry) {
+ if (frequency_index >= frequencies.length ||
+ !telemetry_boxes[telemetry - AltosLib.ao_telemetry_min].isSelected())
+ {
+ frequency_index = 0;
+ do {
+ ++telemetry;
+ if (telemetry > AltosLib.ao_telemetry_max)
+ telemetry = AltosLib.ao_telemetry_min;
+ } while (!telemetry_boxes[telemetry - AltosLib.ao_telemetry_min].isSelected());
+ set_telemetry();
+ }
+ } else {
+ if (frequency_index >= frequencies.length)
+ frequency_index = 0;
+ }
+ set_frequency();
+ set_label();
+ reader.set_monitor(true);
+ }
+
+
+ void close() {
+ if (thread != null && thread.isAlive()) {
+ thread.interrupt();
+ try {
+ thread.join();
+ } catch (InterruptedException ie) {}
+ }
+ thread = null;
+ if (timer != null)
+ timer.stop();
+ setVisible(false);
+ dispose();
+ }
+
+ void tick_timer() throws InterruptedException, TimeoutException {
+ next();
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+
+ try {
+ if (cmd.equals("cancel"))
+ close();
+
+ if (cmd.equals("tick"))
+ tick_timer();
+
+ if (cmd.equals("telemetry")) {
+ int k;
+ int scanning_telemetry = 0;
+ for (k = AltosLib.ao_telemetry_min; k <= AltosLib.ao_telemetry_max; k++) {
+ int j = k - AltosLib.ao_telemetry_min;
+ if (telemetry_boxes[j].isSelected())
+ scanning_telemetry |= (1 << k);
+ }
+ if (scanning_telemetry == 0) {
+ scanning_telemetry |= (1 << AltosLib.ao_telemetry_standard);
+ telemetry_boxes[AltosLib.ao_telemetry_standard - AltosLib.ao_telemetry_min].setSelected(true);
+ }
+ AltosUIPreferences.set_scanning_telemetry(scanning_telemetry);
+ }
+
+ if (cmd.equals("monitor")) {
+ close();
+ AltosScanResult r = (AltosScanResult) (list.getSelectedValue());
+ if (r != null) {
+ if (device != null) {
+ if (reader != null) {
+ reader.set_telemetry(r.telemetry);
+ reader.set_frequency(r.frequency.frequency);
+ reader.save_frequency();
+ owner.scan_device_selected(device);
+ }
+ }
+ }
+ }
+ } catch (TimeoutException te) {
+ close();
+ } catch (InterruptedException ie) {
+ close();
+ }
+ }
+
+ /* A window listener to catch closing events and tell the config code */
+ class ConfigListener extends WindowAdapter {
+ AltosScanUI ui;
+
+ public ConfigListener(AltosScanUI this_ui) {
+ ui = this_ui;
+ }
+
+ public void windowClosing(WindowEvent e) {
+ ui.actionPerformed(new ActionEvent(e.getSource(),
+ ActionEvent.ACTION_PERFORMED,
+ "close"));
+ }
+ }
+
+ private boolean open() {
+ device = AltosDeviceUIDialog.show(owner, AltosLib.product_basestation);
+ if (device == null)
+ return false;
+ try {
+ reader = new AltosTelemetryReader(new AltosSerial(device));
+ set_frequency();
+ set_telemetry();
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ie) {
+ }
+ reader.flush();
+ handler = new TelemetryHandler();
+ thread = new Thread(handler);
+ thread.start();
+ return true;
+ } catch (FileNotFoundException ee) {
+ JOptionPane.showMessageDialog(owner,
+ ee.getMessage(),
+ "Cannot open target device",
+ JOptionPane.ERROR_MESSAGE);
+ } catch (AltosSerialInUseException si) {
+ JOptionPane.showMessageDialog(owner,
+ String.format("Device \"%s\" already in use",
+ device.toShortString()),
+ "Device in use",
+ JOptionPane.ERROR_MESSAGE);
+ } catch (IOException ee) {
+ JOptionPane.showMessageDialog(owner,
+ device.toShortString(),
+ "Unkonwn I/O error",
+ JOptionPane.ERROR_MESSAGE);
+ } catch (TimeoutException te) {
+ JOptionPane.showMessageDialog(owner,
+ device.toShortString(),
+ "Timeout error",
+ JOptionPane.ERROR_MESSAGE);
+ } catch (InterruptedException ie) {
+ JOptionPane.showMessageDialog(owner,
+ device.toShortString(),
+ "Interrupted exception",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ if (reader != null)
+ reader.close(false);
+ return false;
+ }
+
+ public AltosScanUI(AltosUIFrame in_owner, boolean in_select_telemetry) {
+
+ owner = in_owner;
+ select_telemetry = in_select_telemetry;
+
+ frequencies = AltosUIPreferences.common_frequencies();
+ frequency_index = 0;
+
+ telemetry = AltosLib.ao_telemetry_standard;
+
+ if (!open())
+ return;
+
+ Container pane = getContentPane();
+ GridBagConstraints c = new GridBagConstraints();
+ Insets i = new Insets(4,4,4,4);
+
+ timer = new javax.swing.Timer(timeout, this);
+ timer.setActionCommand("tick");
+ timer.restart();
+
+ owner = in_owner;
+
+ pane.setLayout(new GridBagLayout());
+
+ scanning_label = new JLabel("Scanning:");
+ frequency_label = new JLabel("");
+
+ if (select_telemetry) {
+ telemetry_label = new JLabel("");
+ telemetry = AltosLib.ao_telemetry_min;
+ } else {
+ telemetry = AltosLib.ao_telemetry_standard;
+ }
+
+ set_label();
+
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.WEST;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 1;
+
+ c.gridx = 0;
+ c.gridy = 0;
+ c.gridwidth = 2;
+
+ pane.add(scanning_label, c);
+ c.gridy = 1;
+ pane.add(frequency_label, c);
+
+ int y_offset = 3;
+
+ if (select_telemetry) {
+ c.gridy = 2;
+ pane.add(telemetry_label, c);
+
+ int scanning_telemetry = AltosUIPreferences.scanning_telemetry();
+ telemetry_boxes = new JCheckBox[AltosLib.ao_telemetry_max - AltosLib.ao_telemetry_min + 1];
+ for (int k = AltosLib.ao_telemetry_min; k <= AltosLib.ao_telemetry_max; k++) {
+ int j = k - AltosLib.ao_telemetry_min;
+ telemetry_boxes[j] = new JCheckBox(AltosLib.telemetry_name(k));
+ c.gridy = 3 + j;
+ pane.add(telemetry_boxes[j], c);
+ telemetry_boxes[j].setActionCommand("telemetry");
+ telemetry_boxes[j].addActionListener(this);
+ telemetry_boxes[j].setSelected((scanning_telemetry & (1 << k)) != 0);
+ }
+ y_offset += (AltosLib.ao_telemetry_max - AltosLib.ao_telemetry_min + 1);
+ }
+
+ list = new JList<AltosScanResult>(results) {
+ //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);
+ }
+ };
+
+ list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
+ list.setVisibleRowCount(-1);
+
+ list.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ monitor_button.doClick(); //emulate button click
+ }
+ }
+ });
+ JScrollPane listScroller = new JScrollPane(list);
+ listScroller.setPreferredSize(new Dimension(400, 80));
+ listScroller.setAlignmentX(LEFT_ALIGNMENT);
+
+ //Create a container so that we can add a title around
+ //the scroll pane. Can't add a title directly to the
+ //scroll pane because its background would be white.
+ //Lay out the label and scroll pane from top to bottom.
+ JPanel listPane = new JPanel();
+ listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
+
+ JLabel label = new JLabel("Select Device");
+ label.setLabelFor(list);
+ listPane.add(label);
+ listPane.add(Box.createRigidArea(new Dimension(0,5)));
+ listPane.add(listScroller);
+ listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+ c.fill = GridBagConstraints.BOTH;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 1;
+
+ c.gridx = 0;
+ c.gridy = y_offset;
+ c.gridwidth = 2;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(listPane, c);
+
+ cancel_button = new JButton("Cancel");
+ cancel_button.addActionListener(this);
+ cancel_button.setActionCommand("cancel");
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 1;
+
+ c.gridx = 0;
+ c.gridy = y_offset + 1;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(cancel_button, c);
+
+ monitor_button = new JButton("Monitor");
+ monitor_button.addActionListener(this);
+ monitor_button.setActionCommand("monitor");
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 1;
+
+ c.gridx = 1;
+ c.gridy = y_offset + 1;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(monitor_button, c);
+
+ pack();
+ setLocationRelativeTo(owner);
+
+ addWindowListener(new ConfigListener(this));
+
+ setVisible(true);
+ }
+}
diff --git a/altosuilib/AltosSerial.java b/altosuilib/AltosSerial.java
new file mode 100644
index 00000000..60e15bdb
--- /dev/null
+++ b/altosuilib/AltosSerial.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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.
+ */
+
+/*
+ * Deal with TeleDongle on a serial port
+ */
+
+package org.altusmetrum.altosuilib_2;
+
+import java.io.*;
+import java.util.*;
+import java.awt.*;
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+import libaltosJNI.*;
+
+/*
+ * This class reads from the serial port and places each received
+ * line in a queue. Dealing with that queue is left up to other
+ * threads.
+ */
+
+public class AltosSerial extends AltosLink {
+
+ static java.util.List<String> devices_opened = Collections.synchronizedList(new LinkedList<String>());
+
+ public AltosDevice device;
+ SWIGTYPE_p_altos_file altos;
+ Thread input_thread;
+ String line;
+ byte[] line_bytes;
+ int line_count;
+ Frame frame;
+
+ public int getchar() {
+ if (altos == null)
+ return ERROR;
+ return libaltos.altos_getchar(altos, 0);
+ }
+
+ public void flush_output() {
+ super.flush_output();
+ if (altos != null) {
+ if (libaltos.altos_flush(altos) != 0)
+ close_serial();
+ }
+ }
+
+ JDialog timeout_dialog;
+
+ private void start_timeout_dialog_internal() {
+
+ Object[] options = { "Cancel" };
+
+ JOptionPane pane = new JOptionPane();
+ pane.setMessage(String.format("Connecting to %s, %7.3f MHz as %s", device.toShortString(), frequency, callsign));
+ pane.setOptions(options);
+ pane.setInitialValue(null);
+
+ timeout_dialog = pane.createDialog(frame, "Connecting...");
+
+ timeout_dialog.setVisible(true);
+
+ Object o = pane.getValue();
+ if (o == null)
+ return;
+ if (options[0].equals(o))
+ reply_abort = true;
+ timeout_dialog.dispose();
+ timeout_dialog = null;
+ }
+
+ /*
+ * These are required by the AltosLink implementation
+ */
+
+ public boolean can_cancel_reply() {
+ /*
+ * Can cancel any replies not called from the dispatch thread
+ */
+ return !SwingUtilities.isEventDispatchThread();
+ }
+
+ public boolean show_reply_timeout() {
+ if (!SwingUtilities.isEventDispatchThread() && frame != null) {
+ Runnable r = new Runnable() {
+ public void run() {
+ start_timeout_dialog_internal();
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ return true;
+ }
+ return false;
+ }
+
+ public void hide_reply_timeout() {
+ Runnable r = new Runnable() {
+ public void run() {
+ timeout_dialog.setVisible(false);
+ }
+ };
+ SwingUtilities.invokeLater(r);
+ }
+
+ private synchronized void close_serial() {
+ synchronized (devices_opened) {
+ devices_opened.remove(device.getPath());
+ }
+ if (altos != null) {
+ libaltos.altos_free(altos);
+ altos = null;
+ }
+ abort_reply();
+ }
+
+ public void close() {
+ if (remote) {
+ try {
+ stop_remote();
+ } catch (InterruptedException ie) {
+ }
+ }
+ if (in_reply != 0)
+ System.out.printf("Uh-oh. Closing active serial device\n");
+
+ close_serial();
+
+ if (input_thread != null) {
+ try {
+ input_thread.interrupt();
+ input_thread.join();
+ } catch (InterruptedException ie) {
+ }
+ input_thread = null;
+ }
+ if (debug)
+ System.out.printf("Closing %s\n", device.getPath());
+ }
+
+ private void putc(char c) {
+ if (altos != null)
+ if (libaltos.altos_putchar(altos, c) != 0)
+ close_serial();
+ }
+
+ public void putchar(byte c) {
+ if (altos != null) {
+ if (debug)
+ System.out.printf(" %02x", (int) c & 0xff);
+ if (libaltos.altos_putchar(altos, (char) c) != 0)
+ close_serial();
+ }
+ }
+
+ public void print(String data) {
+ for (int i = 0; i < data.length(); i++)
+ putc(data.charAt(i));
+ }
+
+ private void open() throws FileNotFoundException, AltosSerialInUseException {
+ synchronized (devices_opened) {
+ if (devices_opened.contains(device.getPath()))
+ throw new AltosSerialInUseException(device);
+ devices_opened.add(device.getPath());
+ }
+ altos = device.open();
+ if (altos == null) {
+ final String message = device.getErrorString();
+ close();
+ throw new FileNotFoundException(String.format("%s (%s)",
+ device.toShortString(), message));
+ }
+ if (debug)
+ System.out.printf("Open %s\n", device.getPath());
+ input_thread = new Thread(this);
+ input_thread.start();
+ print("~\nE 0\n");
+ set_monitor(false);
+ flush_output();
+ }
+
+ public void set_frame(Frame in_frame) {
+ frame = in_frame;
+ }
+
+ public AltosSerial(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException {
+ device = in_device;
+ frame = null;
+ serial = device.getSerial();
+ name = device.toShortString();
+ open();
+ }
+}
diff --git a/altosuilib/AltosSerialInUseException.java b/altosuilib/AltosSerialInUseException.java
new file mode 100644
index 00000000..1e8207d1
--- /dev/null
+++ b/altosuilib/AltosSerialInUseException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+public class AltosSerialInUseException extends Exception {
+ public AltosDevice device;
+
+ public AltosSerialInUseException (AltosDevice in_device) {
+ device = in_device;
+ }
+}
diff --git a/altosuilib/AltosUIAxis.java b/altosuilib/AltosUIAxis.java
index 1638ea29..74561673 100644
--- a/altosuilib/AltosUIAxis.java
+++ b/altosuilib/AltosUIAxis.java
@@ -15,14 +15,14 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_3.*;
+import org.altusmetrum.altoslib_4.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
@@ -50,7 +50,7 @@ public class AltosUIAxis extends NumberAxis {
public void set_units() {
setLabel(String.format("%s (%s)", label, units.show_units()));
}
-
+
public void set_enable(boolean enable) {
if (enable) {
visible++;
diff --git a/altosuilib/AltosUIConfigure.java b/altosuilib/AltosUIConfigure.java
index 9e72e403..920ed8e2 100644
--- a/altosuilib/AltosUIConfigure.java
+++ b/altosuilib/AltosUIConfigure.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.awt.*;
import java.awt.event.*;
@@ -23,10 +23,10 @@ import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
-class DelegatingRenderer implements ListCellRenderer {
+class DelegatingRenderer implements ListCellRenderer<Object> {
// ...
- public static void install(JComboBox comboBox) {
+ public static void install(JComboBox<Object> comboBox) {
DelegatingRenderer renderer = new DelegatingRenderer(comboBox);
renderer.initialise();
comboBox.setRenderer(renderer);
@@ -36,7 +36,7 @@ class DelegatingRenderer implements ListCellRenderer {
private final JComboBox comboBox;
// ...
- private ListCellRenderer delegate;
+ private ListCellRenderer<? super Object> delegate;
// ...
private DelegatingRenderer(JComboBox comboBox) {
@@ -45,21 +45,22 @@ class DelegatingRenderer implements ListCellRenderer {
// ...
private void initialise() {
- delegate = new JComboBox().getRenderer();
+ JComboBox<Object> c = new JComboBox<Object>();
+ delegate = c.getRenderer();
comboBox.addPropertyChangeListener("UI", new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
- delegate = new JComboBox().getRenderer();
+ delegate = new JComboBox<Object>().getRenderer();
}
});
}
// ...
- public Component getListCellRendererComponent(JList list,
+ public Component getListCellRendererComponent(JList<?> list,
Object value, int index, boolean isSelected, boolean cellHasFocus) {
return delegate.getListCellRendererComponent(list,
- ((UIManager.LookAndFeelInfo) value).getName(),
+ ((UIManager.LookAndFeelInfo)value).getName(),
index, isSelected, cellHasFocus);
}
}
@@ -139,7 +140,7 @@ public class AltosUIConfigure
/* Font size setting */
pane.add(new JLabel("Font size"), constraints(0, 1));
- final JComboBox font_size_value = new JComboBox(font_size_names);
+ final JComboBox<String> font_size_value = new JComboBox<String>(font_size_names);
int font_size = AltosUIPreferences.font_size();
font_size_value.setSelectedIndex(font_size - AltosUILib.font_size_small);
font_size_value.addActionListener(new ActionListener() {
@@ -181,7 +182,7 @@ public class AltosUIConfigure
final UIManager.LookAndFeelInfo[] look_and_feels = UIManager.getInstalledLookAndFeels();
- final JComboBox look_and_feel_value = new JComboBox(look_and_feels);
+ final JComboBox<Object> look_and_feel_value = new JComboBox<Object>(look_and_feels);
DelegatingRenderer.install(look_and_feel_value);
@@ -228,8 +229,8 @@ public class AltosUIConfigure
public void add_frequencies() {
}
- public AltosUIConfigure(JFrame in_owner) {
- super(in_owner, "Configure AltosUI", false);
+ public AltosUIConfigure(JFrame in_owner, String name, String label) {
+ super(in_owner, name, false);
owner = in_owner;
pane = getContentPane();
@@ -238,7 +239,7 @@ public class AltosUIConfigure
row = 0;
/* Nice label at the top */
- pane.add(new JLabel ("Configure AltOS UI"),
+ pane.add(new JLabel (label),
constraints(0, 3));
row++;
@@ -270,4 +271,8 @@ public class AltosUIConfigure
setLocationRelativeTo(owner);
setVisible(true);
}
+
+ public AltosUIConfigure(JFrame in_owner) {
+ this(in_owner, "Configure AltosUI", "Configure AltOS UI");
+ }
}
diff --git a/altosuilib/AltosUIDataMissing.java b/altosuilib/AltosUIDataMissing.java
index c7b01859..353ff30f 100644
--- a/altosuilib/AltosUIDataMissing.java
+++ b/altosuilib/AltosUIDataMissing.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
public class AltosUIDataMissing extends Exception {
public int id;
diff --git a/altosuilib/AltosUIDataPoint.java b/altosuilib/AltosUIDataPoint.java
index d3020410..3f16500e 100644
--- a/altosuilib/AltosUIDataPoint.java
+++ b/altosuilib/AltosUIDataPoint.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
public interface AltosUIDataPoint {
public abstract double x() throws AltosUIDataMissing;
diff --git a/altosuilib/AltosUIDataSet.java b/altosuilib/AltosUIDataSet.java
index 6f23ef9a..ee70a3fd 100644
--- a/altosuilib/AltosUIDataSet.java
+++ b/altosuilib/AltosUIDataSet.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
public interface AltosUIDataSet {
public abstract String name();
diff --git a/altosuilib/AltosUIDialog.java b/altosuilib/AltosUIDialog.java
index e1e699a7..dc737414 100644
--- a/altosuilib/AltosUIDialog.java
+++ b/altosuilib/AltosUIDialog.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.awt.*;
import java.awt.event.*;
diff --git a/altosuilib/AltosUIEnable.java b/altosuilib/AltosUIEnable.java
index ea4bd00a..da98797a 100644
--- a/altosuilib/AltosUIEnable.java
+++ b/altosuilib/AltosUIEnable.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.awt.*;
import java.awt.event.*;
@@ -23,7 +23,7 @@ import javax.swing.*;
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
-import org.altusmetrum.altoslib_3.*;
+import org.altusmetrum.altoslib_4.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
@@ -45,7 +45,7 @@ public class AltosUIEnable extends Container {
class GraphElement implements ActionListener {
AltosUIGrapher grapher;
- JRadioButton enable;
+ JCheckBox enable;
String name;
public void actionPerformed(ActionEvent ae) {
@@ -55,8 +55,8 @@ public class AltosUIEnable extends Container {
GraphElement (String name, AltosUIGrapher grapher, boolean enabled) {
this.name = name;
this.grapher = grapher;
- enable = new JRadioButton(name, enabled);
- grapher.set_enable(enabled);
+ enable = new JCheckBox(name, enabled);
+ grapher.set_enable(enabled);
enable.addActionListener(this);
}
}
@@ -86,10 +86,10 @@ public class AltosUIEnable extends Container {
/* Imperial units setting */
/* Add label */
- JRadioButton imperial_units = new JRadioButton("Imperial Units", AltosUIPreferences.imperial_units());
+ JCheckBox imperial_units = new JCheckBox("Imperial Units", AltosUIPreferences.imperial_units());
imperial_units.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- JRadioButton item = (JRadioButton) e.getSource();
+ JCheckBox item = (JCheckBox) e.getSource();
boolean enabled = item.isSelected();
AltosUIPreferences.set_imperial_units(enabled);
}
diff --git a/altosuilib/AltosUIFlightTab.java b/altosuilib/AltosUIFlightTab.java
new file mode 100644
index 00000000..039d83e0
--- /dev/null
+++ b/altosuilib/AltosUIFlightTab.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.util.*;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+
+public abstract class AltosUIFlightTab extends JComponent implements AltosFlightDisplay, HierarchyListener {
+ public GridBagLayout layout;
+
+ AltosState last_state;
+ AltosListenerState last_listener_state;
+
+ LinkedList<AltosUIIndicator> indicators = new LinkedList<AltosUIIndicator>();
+
+ public void add (AltosUIIndicator indicator) {
+ indicators.add(indicator);
+ }
+
+ public void remove(AltosUIIndicator indicator) {
+ indicators.remove(indicator);
+ }
+
+ public void reset() {
+ for (AltosUIIndicator i : indicators)
+ i.reset();
+ }
+
+ public void font_size_changed(int font_size) {
+ for (AltosUIIndicator i : indicators)
+ i.font_size_changed(font_size);
+ }
+
+ public void units_changed(boolean imperial_units) {
+ for (AltosUIIndicator i : indicators)
+ i.units_changed(imperial_units);
+ }
+
+ public void show(AltosState state, AltosListenerState listener_state) {
+ if (!isShowing()) {
+ last_state = state;
+ last_listener_state = listener_state;
+ return;
+ }
+
+ for (AltosUIIndicator i : indicators)
+ i.show(state, listener_state);
+ }
+
+ public void hierarchyChanged(HierarchyEvent e) {
+ if (last_state != null && isShowing()) {
+ AltosState state = last_state;
+ AltosListenerState listener_state = last_listener_state;
+
+ last_state = null;
+ last_listener_state = null;
+ show(state, listener_state);
+ }
+ }
+
+ abstract public String getName();
+
+ public AltosUIFlightTab() {
+ layout = new GridBagLayout();
+
+ setLayout(layout);
+
+ addHierarchyListener(this);
+ }
+}
diff --git a/altosuilib/AltosUIFrame.java b/altosuilib/AltosUIFrame.java
index 3fc99910..6e62c762 100644
--- a/altosuilib/AltosUIFrame.java
+++ b/altosuilib/AltosUIFrame.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.awt.*;
import java.awt.event.*;
@@ -45,7 +45,7 @@ public class AltosUIFrame extends JFrame implements AltosUIListener, AltosPositi
};
static public String[] icon_names;
-
+
static public void set_icon_names(String[] new_icon_names) { icon_names = new_icon_names; }
public String[] icon_names() {
@@ -57,7 +57,7 @@ public class AltosUIFrame extends JFrame implements AltosUIListener, AltosPositi
public void set_icon() {
ArrayList<Image> icons = new ArrayList<Image>();
String[] icon_names = icon_names();
-
+
for (int i = 0; i < icon_names.length; i++) {
java.net.URL imgURL = AltosUIFrame.class.getResource(icon_names[i]);
if (imgURL != null)
@@ -65,14 +65,17 @@ public class AltosUIFrame extends JFrame implements AltosUIListener, AltosPositi
}
setIconImages(icons);
}
-
+
private boolean location_by_platform = true;
public void setLocationByPlatform(boolean lbp) {
location_by_platform = lbp;
super.setLocationByPlatform(lbp);
}
-
+
+ public void scan_device_selected(AltosDevice device) {
+ }
+
public void setSize() {
/* Smash sizes around so that the window comes up in the right shape */
Insets i = getInsets();
@@ -153,7 +156,7 @@ public class AltosUIFrame extends JFrame implements AltosUIListener, AltosPositi
setPosition(position);
}
}
-
+
void init() {
AltosUIPreferences.register_ui_listener(this);
AltosUIPreferences.register_position_listener(this);
diff --git a/altosuilib/AltosUIGraph.java b/altosuilib/AltosUIGraph.java
index 061a7629..9cca088d 100644
--- a/altosuilib/AltosUIGraph.java
+++ b/altosuilib/AltosUIGraph.java
@@ -15,14 +15,14 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_3.*;
+import org.altusmetrum.altoslib_4.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
@@ -82,11 +82,9 @@ public class AltosUIGraph implements AltosUnitsListener {
public void addSeries(String label, int fetch, AltosUnits units, Color color) {
addSeries(label, fetch, units, color, true, newAxis(label, units, color));
}
-
+
public void addMarker(String label, int fetch, Color color) {
AltosUIMarker marker = new AltosUIMarker(fetch, color, plot);
- if (enable != null)
- enable.add(label, marker, true);
this.graphers.add(marker);
}
@@ -131,7 +129,7 @@ public class AltosUIGraph implements AltosUnitsListener {
this.axis_index = 0;
xAxis = new NumberAxis("Time (s)");
-
+
xAxis.setAutoRangeIncludesZero(true);
plot = new XYPlot();
@@ -158,4 +156,4 @@ public class AltosUIGraph implements AltosUnitsListener {
AltosPreferences.register_units_listener(this);
}
-} \ No newline at end of file
+}
diff --git a/altosuilib/AltosUIGrapher.java b/altosuilib/AltosUIGrapher.java
index 23e7d9f0..724fac18 100644
--- a/altosuilib/AltosUIGrapher.java
+++ b/altosuilib/AltosUIGrapher.java
@@ -15,14 +15,14 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_3.*;
+import org.altusmetrum.altoslib_4.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
@@ -37,7 +37,7 @@ import org.jfree.data.*;
interface AltosUIGrapher {
public abstract void set_units();
-
+
public abstract void clear();
public abstract void add(AltosUIDataPoint dataPoint);
diff --git a/altosuilib/AltosUIIndicator.java b/altosuilib/AltosUIIndicator.java
new file mode 100644
index 00000000..b1626cba
--- /dev/null
+++ b/altosuilib/AltosUIIndicator.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+
+public abstract class AltosUIIndicator implements AltosFontListener, AltosUnitsListener {
+ JLabel label;
+ JTextField[] values;
+ AltosLights lights;
+ int number_values;
+ boolean has_lights;
+ int value_width;
+
+ abstract public void show(AltosState state, AltosListenerState listener_state);
+
+ public void set_lights(boolean on) {
+ lights.set(on);
+ }
+
+ public void setVisible(boolean visible) {
+ if (lights != null)
+ lights.setVisible(visible);
+ label.setVisible(visible);
+ for (int i = 0; i < values.length; i++)
+ values[i].setVisible(visible);
+ }
+
+ public void reset() {
+ for (int i = 0; i < values.length; i++)
+ values[i].setText("");
+ if (lights != null)
+ lights.set(false);
+ }
+
+ public void show() {
+ if (lights != null)
+ lights.setVisible(true);
+ label.setVisible(true);
+ for (int i = 0; i < values.length; i++)
+ values[i].setVisible(true);
+ }
+
+ public void show(String... s) {
+ int n = Math.min(s.length, values.length);
+
+ show();
+ for (int i = 0; i < n; i++)
+ values[i].setText(s[i]);
+ }
+
+ public void show(String format, double value) {
+ show(String.format(format, value));
+ }
+
+ public void show(String format, int value) {
+ show(String.format(format, value));
+ }
+
+ public void show(String format1, double value1, String format2, double value2) {
+ show(String.format(format1, value1), String.format(format2, value2));
+ }
+
+ public void show(String format1, int value1, String format2, int value2) {
+ show(String.format(format1, value1), String.format(format2, value2));
+ }
+
+ public void hide() {
+ if (lights != null)
+ lights.setVisible(false);
+ label.setVisible(false);
+ for (int i = 0; i < values.length; i++)
+ values[i].setVisible(false);
+ }
+
+ public void font_size_changed(int font_size) {
+ label.setFont(AltosUILib.label_font);
+ for (int i = 0; i < values.length; i++)
+ values[i].setFont(AltosUILib.value_font);
+ }
+
+ public void units_changed(boolean imperial_units) {
+ }
+
+ public void set_label(String text) {
+ label.setText(text);
+ }
+
+ public void remove(Container container) {
+ if (lights != null)
+ container.remove(lights);
+ container.remove(label);
+ for (int i = 0; i < values.length; i++)
+ container.remove(values[i]);
+ }
+
+ public AltosUIIndicator (Container container, int x, int y, int label_width, String text, int number_values, boolean has_lights, int value_width, int value_space) {
+ GridBagLayout layout = (GridBagLayout)(container.getLayout());
+
+ GridBagConstraints c = new GridBagConstraints();
+ c.weighty = 1;
+
+ if (has_lights) {
+ lights = new AltosLights();
+ c.gridx = x; c.gridy = y;
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.VERTICAL;
+ c.weightx = 0;
+ layout.setConstraints(lights, c);
+ container.add(lights);
+ }
+
+ label = new JLabel(text);
+ label.setFont(AltosUILib.label_font);
+ label.setHorizontalAlignment(SwingConstants.LEFT);
+ c.gridx = x + 1; c.gridy = y;
+ c.gridwidth = label_width;
+ c.insets = new Insets(AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad);
+ c.anchor = GridBagConstraints.WEST;
+ c.fill = GridBagConstraints.VERTICAL;
+ c.weightx = 0;
+ layout.setConstraints(label, c);
+ container.add(label);
+
+ values = new JTextField[number_values];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = new JTextField(AltosUILib.text_width);
+ values[i].setEditable(false);
+ values[i].setFont(AltosUILib.value_font);
+ values[i].setHorizontalAlignment(SwingConstants.RIGHT);
+ c.gridx = 1 + label_width + x + i * value_space; c.gridy = y;
+ c.anchor = GridBagConstraints.WEST;
+ c.fill = GridBagConstraints.BOTH;
+ c.weightx = 1;
+ c.gridwidth = value_width;
+ layout.setConstraints(values[i], c);
+ container.add(values[i]);
+ }
+ }
+
+ public AltosUIIndicator (Container container, int x, int y, int label_width, String text, int number_values, boolean has_lights, int value_width) {
+ this(container, x, y, label_width, text, number_values, has_lights, value_width, 1);
+ }
+
+ public AltosUIIndicator (Container container, int x, int y, String text, int number_values, boolean has_lights, int value_width) {
+ this(container, x, y, 1, text, number_values, has_lights, value_width);
+ }
+
+ public AltosUIIndicator (Container container, int y, String text, int number_values, boolean has_lights, int value_width) {
+ this(container, 0, y, text, number_values, has_lights, value_width);
+ }
+
+ public AltosUIIndicator (Container container, int y, String text, int number_values, boolean has_lights) {
+ this(container, 0, y, text, number_values, has_lights, 1);
+ }
+
+ public AltosUIIndicator (Container container, int y, String text, int number_values) {
+ this(container, 0, y, text, number_values, false, 1);
+ }
+
+ public AltosUIIndicator (Container container, int y, String text) {
+ this(container, 0, y, text, 1, false, 1);
+ }
+}
diff --git a/altosuilib/AltosUILatLon.java b/altosuilib/AltosUILatLon.java
new file mode 100644
index 00000000..688dd58b
--- /dev/null
+++ b/altosuilib/AltosUILatLon.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.Math;
+import java.awt.geom.*;
+import java.util.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosUILatLon {
+ public double lat;
+ public double lon;
+
+ public boolean equals(AltosUILatLon other) {
+ if (other == null)
+ return false;
+ return lat == other.lat && lon == other.lon;
+ }
+
+ public AltosUILatLon(double lat, double lon) {
+ this.lat = lat;
+ this.lon = lon;
+ }
+}
diff --git a/altosuilib/AltosUILib.java b/altosuilib/AltosUILib.java
index 76782e2e..b51c5963 100644
--- a/altosuilib/AltosUILib.java
+++ b/altosuilib/AltosUILib.java
@@ -15,12 +15,12 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.awt.*;
import libaltosJNI.*;
-import org.altusmetrum.altoslib_3.*;
+import org.altusmetrum.altoslib_4.*;
public class AltosUILib extends AltosLib {
diff --git a/altosuilib/AltosUIListener.java b/altosuilib/AltosUIListener.java
index 450dc0bf..75a0ad94 100644
--- a/altosuilib/AltosUIListener.java
+++ b/altosuilib/AltosUIListener.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
public interface AltosUIListener {
public void ui_changed(String look_and_feel);
diff --git a/altosuilib/AltosUIMap.java b/altosuilib/AltosUIMap.java
new file mode 100644
index 00000000..aaa68f23
--- /dev/null
+++ b/altosuilib/AltosUIMap.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.Math;
+import java.awt.geom.*;
+import java.util.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosUIMapZoomListener {
+
+ static final int px_size = 512;
+
+ static final int maptype_hybrid = 0;
+ static final int maptype_roadmap = 1;
+ static final int maptype_satellite = 2;
+ static final int maptype_terrain = 3;
+ static final int maptype_default = maptype_hybrid;
+
+ static final String[] maptype_names = {
+ "hybrid",
+ "roadmap",
+ "satellite",
+ "terrain"
+ };
+
+ public static final String[] maptype_labels = {
+ "Hybrid",
+ "Roadmap",
+ "Satellite",
+ "Terrain"
+ };
+
+ public static final Color stateColors[] = {
+ Color.WHITE, // startup
+ Color.WHITE, // idle
+ Color.WHITE, // pad
+ Color.RED, // boost
+ Color.PINK, // fast
+ Color.YELLOW, // coast
+ Color.CYAN, // drogue
+ Color.BLUE, // main
+ Color.BLACK, // landed
+ Color.BLACK, // invalid
+ Color.CYAN, // stateless
+ };
+
+ public void reset() {
+ // nothing
+ }
+
+ public void font_size_changed(int font_size) {
+ view.set_font();
+ }
+
+ public void units_changed(boolean imperial_units) {
+ view.set_units();
+ }
+
+ JLabel zoom_label;
+
+ private void set_zoom_label() {
+ zoom_label.setText(String.format("Zoom %d", view.zoom() - view.default_zoom));
+ }
+
+ public void zoom_changed(int zoom) {
+ set_zoom_label();
+ }
+
+ public void set_zoom(int zoom) {
+ view.set_zoom(zoom);
+ }
+
+ public int get_zoom() {
+ return view.zoom();
+ }
+
+ public void set_maptype(int type) {
+ view.set_maptype(type);
+ maptype_combo.setSelectedIndex(type);
+ }
+
+ public void show(AltosState state, AltosListenerState listener_state) {
+ view.show(state, listener_state);
+ }
+
+ public void centre(double lat, double lon) {
+ view.centre(lat, lon);
+ }
+
+ public void centre(AltosState state) {
+ if (!state.gps.locked && state.gps.nsat < 4)
+ return;
+ centre(state.gps.lat, state.gps.lon);
+ }
+
+ public void add_mark(double lat, double lon, int state) {
+ view.add_mark(lat, lon, state);
+ }
+
+ public void clear_marks() {
+ view.clear_marks();
+ }
+
+ AltosUIMapView view;
+
+ private GridBagLayout layout = new GridBagLayout();
+
+ JComboBox<String> maptype_combo;
+
+ public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) {
+ view.set_load_params(lat, lon, radius, listener);
+ }
+
+ public boolean all_fetched() {
+ return view.all_fetched();
+ }
+
+ public static void prefetch_maps(double lat, double lon) {
+ }
+
+ public String getName() {
+ return "Map";
+ }
+
+ public AltosUIMap() {
+
+ view = new AltosUIMapView();
+
+ view.setPreferredSize(new Dimension(500,500));
+ view.setVisible(true);
+ view.setEnabled(true);
+ view.add_zoom_listener(this);
+
+ GridBagLayout my_layout = new GridBagLayout();
+
+ setLayout(my_layout);
+
+ GridBagConstraints c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.BOTH;
+ c.gridx = 0;
+ c.gridy = 0;
+ c.gridwidth = 1;
+ c.gridheight = 10;
+ c.weightx = 1;
+ c.weighty = 1;
+ add(view, c);
+
+ int y = 0;
+
+ zoom_label = new JLabel("", JLabel.CENTER);
+ set_zoom_label();
+
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(zoom_label, c);
+
+ JButton zoom_reset = new JButton("0");
+ zoom_reset.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ set_zoom(view.default_zoom);
+ }
+ });
+
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(zoom_reset, c);
+
+ JButton zoom_in = new JButton("+");
+ zoom_in.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ set_zoom(get_zoom() + 1);
+ }
+ });
+
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(zoom_in, c);
+
+ JButton zoom_out = new JButton("-");
+ zoom_out.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ set_zoom(get_zoom() - 1);
+ }
+ });
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(zoom_out, c);
+
+ maptype_combo = new JComboBox<String>(maptype_labels);
+
+ maptype_combo.setEditable(false);
+ maptype_combo.setMaximumRowCount(maptype_combo.getItemCount());
+ maptype_combo.addItemListener(new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ view.set_maptype(maptype_combo.getSelectedIndex());
+ }
+ });
+
+ c = new GridBagConstraints();
+ c.anchor = GridBagConstraints.CENTER;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridx = 1;
+ c.gridy = y++;
+ c.weightx = 0;
+ c.weighty = 0;
+ add(maptype_combo, c);
+ }
+}
diff --git a/altosuilib/AltosUIMapCache.java b/altosuilib/AltosUIMapCache.java
new file mode 100644
index 00000000..55311d8c
--- /dev/null
+++ b/altosuilib/AltosUIMapCache.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import javax.swing.*;
+import javax.imageio.ImageIO;
+import java.awt.image.*;
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+
+public class AltosUIMapCache {
+ static final int success = 0;
+ static final int loading = 1;
+ static final int failed = 2;
+ static final int bad_request = 3;
+ static final int forbidden = 4;
+
+ static final int min_cache_size = 9;
+ static final int max_cache_size = 24;
+
+ private Object fetch_lock = new Object();
+ private Object cache_lock = new Object();
+
+ int cache_size = min_cache_size;
+
+ AltosUIMapImage[] images = new AltosUIMapImage[cache_size];
+
+ long used;
+
+ public void set_cache_size(int new_size) {
+ if (new_size < min_cache_size)
+ new_size = min_cache_size;
+ if (new_size > max_cache_size)
+ new_size = max_cache_size;
+ if (new_size == cache_size)
+ return;
+
+ synchronized(cache_lock) {
+ AltosUIMapImage[] new_images = new AltosUIMapImage[new_size];
+
+ for (int i = 0; i < cache_size; i++) {
+ if (i < new_size)
+ new_images[i] = images[i];
+ else if (images[i] != null)
+ images[i].flush();
+ }
+ images = new_images;
+ cache_size = new_size;
+ }
+ }
+
+ public Image get(AltosUIMapTile tile, AltosUIMapStore store, int width, int height) {
+ int oldest = -1;
+ long age = used;
+
+ synchronized(cache_lock) {
+ AltosUIMapImage image = null;
+ for (int i = 0; i < cache_size; i++) {
+ image = images[i];
+
+ if (image == null) {
+ oldest = i;
+ break;
+ }
+ if (store.equals(image.store)) {
+ image.used = used++;
+ return image.image;
+ }
+ if (image.used < age) {
+ oldest = i;
+ age = image.used;
+ }
+ }
+
+ try {
+ image = new AltosUIMapImage(tile, store);
+ image.used = used++;
+ if (images[oldest] != null)
+ images[oldest].flush();
+
+ images[oldest] = image;
+
+ if (image.image == null)
+ tile.set_status(loading);
+ else
+ tile.set_status(success);
+
+ return image.image;
+ } catch (IOException e) {
+ tile.set_status(failed);
+ return null;
+ }
+ }
+ }
+
+ public AltosUIMapCache() {
+ }
+}
diff --git a/altosuilib/AltosUIMapImage.java b/altosuilib/AltosUIMapImage.java
new file mode 100644
index 00000000..3819d079
--- /dev/null
+++ b/altosuilib/AltosUIMapImage.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import javax.swing.*;
+import javax.imageio.ImageIO;
+import java.awt.image.*;
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+
+public class AltosUIMapImage implements AltosUIMapStoreListener {
+ static final long google_maps_ratelimit_ms = 1200;
+ // Google limits static map queries to 50 per minute per IP, so
+ // each query should take at least 1.2 seconds.
+
+ static final int success = 0;
+ static final int loading = 1;
+ static final int failed = 2;
+ static final int bad_request = 3;
+ static final int forbidden = 4;
+
+ static long forbidden_time;
+ static boolean forbidden_set = false;
+ static final long forbidden_interval = 60l * 1000l * 1000l * 1000l;
+
+ AltosUIMapTile tile; /* Notify when image has been loaded */
+ Image image;
+ AltosUIMapStore store;
+ long used;
+
+ class loader implements Runnable {
+ public void run() {
+ if (image != null)
+ tile.notify_image(image);
+ try {
+ image = ImageIO.read(store.file);
+ } catch (Exception ex) {
+ }
+ if (image == null)
+ tile.set_status(failed);
+ else
+ tile.set_status(success);
+ tile.notify_image(image);
+ }
+ }
+
+ private void load() {
+ loader l = new loader();
+ Thread lt = new Thread(l);
+ lt.start();
+ }
+
+ public void flush() {
+ if (image != null) {
+ image.flush();
+ image = null;
+ }
+ }
+
+ public boolean has_map() {
+ return store.status() == AltosUIMapStore.success;
+ }
+
+ public synchronized void notify_store(AltosUIMapStore store, int status) {
+ switch (status) {
+ case AltosUIMapStore.loading:
+ break;
+ case AltosUIMapStore.success:
+ load();
+ break;
+ default:
+ tile.set_status(status);
+ tile.notify_image(null);
+ }
+ }
+
+ public AltosUIMapImage(AltosUIMapTile tile, AltosUIMapStore store) throws IOException {
+ this.tile = tile;
+ this.image = null;
+ this.store = store;
+ this.used = 0;
+
+ int status = store.status();
+ switch (status) {
+ case AltosUIMapStore.loading:
+ store.add_listener(this);
+ break;
+ case AltosUIMapStore.success:
+ load();
+ break;
+ default:
+ tile.set_status(status);
+ tile.notify_image(null);
+ break;
+ }
+ }
+}
diff --git a/altosuilib/AltosUIMapLine.java b/altosuilib/AltosUIMapLine.java
new file mode 100644
index 00000000..e09a2d9f
--- /dev/null
+++ b/altosuilib/AltosUIMapLine.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.Math;
+import java.awt.geom.*;
+import java.util.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosUIMapLine {
+ AltosUILatLon start, end;
+
+ private Font font = null;
+
+ public void set_font(Font font) {
+ this.font = font;
+ }
+
+ private AltosUILatLon lat_lon(MouseEvent e, AltosUIMapTransform t) {
+ return t.screen_lat_lon(e.getPoint());
+ }
+
+ public void dragged(MouseEvent e, AltosUIMapTransform t) {
+ end = lat_lon(e, t);
+ }
+
+ public void pressed(MouseEvent e, AltosUIMapTransform t) {
+ start = lat_lon(e, t);
+ end = null;
+ }
+
+ private String line_dist() {
+ String format;
+ AltosGreatCircle g = new AltosGreatCircle(start.lat, start.lon,
+ end.lat, end.lon);
+ double distance = g.distance;
+
+ if (AltosConvert.imperial_units) {
+ distance = AltosConvert.meters_to_feet(distance);
+ if (distance < 10000) {
+ format = "%4.0fft";
+ } else {
+ distance /= 5280;
+ if (distance < 10)
+ format = "%5.3fmi";
+ else if (distance < 100)
+ format = "%5.2fmi";
+ else if (distance < 1000)
+ format = "%5.1fmi";
+ else
+ format = "%5.0fmi";
+ }
+ } else {
+ if (distance < 10000) {
+ format = "%4.0fm";
+ } else {
+ distance /= 1000;
+ if (distance < 100)
+ format = "%5.2fkm";
+ else if (distance < 1000)
+ format = "%5.1fkm";
+ else
+ format = "%5.0fkm";
+ }
+ }
+ return String.format(format, distance);
+ }
+
+ public void paint(Graphics2D g, AltosUIMapTransform t) {
+ g.setColor(Color.BLUE);
+
+ if (start == null || end == null)
+ return;
+
+ Line2D.Double line = new Line2D.Double(t.screen(start),
+ t.screen(end));
+
+ g.draw(line);
+
+ String message = line_dist();
+ g.setFont(font);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ Rectangle2D bounds;
+ bounds = font.getStringBounds(message, g.getFontRenderContext());
+
+ float x = (float) line.x1;
+ float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f;
+
+ if (line.x1 < line.x2) {
+ x -= (float) bounds.getWidth() + 2.0f;
+ } else {
+ x += 2.0f;
+ }
+ g.drawString(message, x, y);
+ }
+}
diff --git a/altosuilib/AltosUIMapMark.java b/altosuilib/AltosUIMapMark.java
new file mode 100644
index 00000000..8c640e5f
--- /dev/null
+++ b/altosuilib/AltosUIMapMark.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.Math;
+import java.awt.geom.*;
+import java.util.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosUIMapMark {
+
+ AltosUILatLon lat_lon;
+ int state;
+
+ static public int stroke_width = 6;
+
+ public void paint(Graphics2D g, AltosUIMapTransform t) {
+
+ Point2D.Double pt = t.screen(lat_lon);
+
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+ if (0 <= state && state < AltosUIMap.stateColors.length)
+ g.setColor(AltosUIMap.stateColors[state]);
+ else
+ g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
+
+ g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10);
+ g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40);
+ g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70);
+ }
+
+ public AltosUIMapMark (double lat, double lon, int state) {
+ lat_lon = new AltosUILatLon(lat, lon);
+ this.state = state;
+ }
+}
diff --git a/altosuilib/AltosUIMapPath.java b/altosuilib/AltosUIMapPath.java
new file mode 100644
index 00000000..ff17be67
--- /dev/null
+++ b/altosuilib/AltosUIMapPath.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.Math;
+import java.awt.geom.*;
+import java.util.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+class PathPoint {
+ AltosUILatLon lat_lon;
+ int state;
+
+ public PathPoint(AltosUILatLon lat_lon, int state) {
+ this.lat_lon = lat_lon;
+ this.state = state;
+ }
+
+ public boolean equals(PathPoint other) {
+ if (other == null)
+ return false;
+
+ return lat_lon.equals(other.lat_lon) && state == other.state;
+ }
+}
+
+public class AltosUIMapPath {
+
+ LinkedList<PathPoint> points = new LinkedList<PathPoint>();
+ PathPoint last_point = null;
+
+ static public int stroke_width = 6;
+
+ public void paint(Graphics2D g, AltosUIMapTransform t) {
+ Point2D.Double prev = null;
+
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+ for (PathPoint point : points) {
+ Point2D.Double cur = t.screen(point.lat_lon);
+ if (prev != null) {
+ Line2D.Double line = new Line2D.Double (prev, cur);
+ Rectangle bounds = line.getBounds();
+
+ if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) {
+ if (0 <= point.state && point.state < AltosUIMap.stateColors.length)
+ g.setColor(AltosUIMap.stateColors[point.state]);
+ else
+ g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
+
+ g.draw(line);
+ }
+ }
+ prev = cur;
+ }
+ }
+
+ public AltosUIMapRectangle add(double lat, double lon, int state) {
+ PathPoint point = new PathPoint(new AltosUILatLon (lat, lon), state);
+ AltosUIMapRectangle rect = null;
+
+ if (!point.equals(last_point)) {
+ if (last_point != null)
+ rect = new AltosUIMapRectangle(last_point.lat_lon, point.lat_lon);
+ points.add (point);
+ last_point = point;
+ }
+ return rect;
+ }
+
+ public void clear () {
+ points = new LinkedList<PathPoint>();
+ }
+}
diff --git a/altosuilib/AltosUIMapPreload.java b/altosuilib/AltosUIMapPreload.java
new file mode 100644
index 00000000..56066d70
--- /dev/null
+++ b/altosuilib/AltosUIMapPreload.java
@@ -0,0 +1,607 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.lang.Math;
+import java.net.URL;
+import java.net.URLConnection;
+import org.altusmetrum.altoslib_4.*;
+
+class AltosUIMapPos extends Box {
+ AltosUIFrame owner;
+ JLabel label;
+ JComboBox hemi;
+ JTextField deg;
+ JLabel deg_label;
+ JTextField min;
+ JLabel min_label;
+
+ public void set_value(double new_value) {
+ double d, m;
+ int h;
+
+ h = 0;
+ if (new_value < 0) {
+ h = 1;
+ new_value = -new_value;
+ }
+ d = Math.floor(new_value);
+ deg.setText(String.format("%3.0f", d));
+ m = (new_value - d) * 60.0;
+ min.setText(String.format("%7.4f", m));
+ hemi.setSelectedIndex(h);
+ }
+
+ public double get_value() throws NumberFormatException {
+ int h = hemi.getSelectedIndex();
+ String d_t = deg.getText();
+ String m_t = min.getText();
+ double d, m, v;
+ try {
+ d = Double.parseDouble(d_t);
+ } catch (NumberFormatException ne) {
+ JOptionPane.showMessageDialog(owner,
+ String.format("Invalid degrees \"%s\"",
+ d_t),
+ "Invalid number",
+ JOptionPane.ERROR_MESSAGE);
+ throw ne;
+ }
+ try {
+ if (m_t.equals(""))
+ m = 0;
+ else
+ m = Double.parseDouble(m_t);
+ } catch (NumberFormatException ne) {
+ JOptionPane.showMessageDialog(owner,
+ String.format("Invalid minutes \"%s\"",
+ m_t),
+ "Invalid number",
+ JOptionPane.ERROR_MESSAGE);
+ throw ne;
+ }
+ v = d + m/60.0;
+ if (h == 1)
+ v = -v;
+ return v;
+ }
+
+ public AltosUIMapPos(AltosUIFrame in_owner,
+ String label_value,
+ String[] hemi_names,
+ double default_value) {
+ super(BoxLayout.X_AXIS);
+ owner = in_owner;
+ label = new JLabel(label_value);
+ hemi = new JComboBox<String>(hemi_names);
+ hemi.setEditable(false);
+ deg = new JTextField(5);
+ deg.setMinimumSize(deg.getPreferredSize());
+ deg.setHorizontalAlignment(JTextField.RIGHT);
+ deg_label = new JLabel("°");
+ min = new JTextField(9);
+ min.setMinimumSize(min.getPreferredSize());
+ min_label = new JLabel("'");
+ set_value(default_value);
+ add(label);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(hemi);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(deg);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(deg_label);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(min);
+ add(Box.createRigidArea(new Dimension(5, 0)));
+ add(min_label);
+ }
+}
+
+class AltosUISite {
+ String name;
+ double latitude;
+ double longitude;
+
+ public String toString() {
+ return name;
+ }
+
+ public AltosUISite(String in_name, double in_latitude, double in_longitude) {
+ name = in_name;
+ latitude = in_latitude;
+ longitude = in_longitude;
+ }
+
+ public AltosUISite(String line) throws ParseException {
+ String[] elements = line.split(":");
+
+ if (elements.length < 3)
+ throw new ParseException(String.format("Invalid site line %s", line), 0);
+
+ name = elements[0];
+
+ try {
+ latitude = Double.parseDouble(elements[1]);
+ longitude = Double.parseDouble(elements[2]);
+ } catch (NumberFormatException ne) {
+ throw new ParseException(String.format("Invalid site line %s", line), 0);
+ }
+ }
+}
+
+class AltosUISites extends Thread {
+ AltosUIMapPreload preload;
+ URL url;
+ LinkedList<AltosUISite> sites;
+
+ void notify_complete() {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ preload.set_sites();
+ }
+ });
+ }
+
+ void add(AltosUISite site) {
+ sites.add(site);
+ }
+
+ void add(String line) {
+ try {
+ add(new AltosUISite(line));
+ } catch (ParseException pe) {
+ }
+ }
+
+ public void run() {
+ try {
+ URLConnection uc = url.openConnection();
+ //int length = uc.getContentLength();
+
+ InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.unicode_set);
+ BufferedReader in = new BufferedReader(in_stream);
+
+ for (;;) {
+ String line = in.readLine();
+ if (line == null)
+ break;
+ add(line);
+ }
+ } catch (IOException e) {
+ } finally {
+ notify_complete();
+ }
+ }
+
+ public AltosUISites(AltosUIMapPreload in_preload) {
+ sites = new LinkedList<AltosUISite>();
+ preload = in_preload;
+ try {
+ url = new URL(AltosLib.launch_sites_url);
+ } catch (java.net.MalformedURLException e) {
+ notify_complete();
+ }
+ start();
+ }
+}
+
+public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, ItemListener, AltosUIMapTileListener {
+ AltosUIFrame owner;
+ AltosUIMap map;
+ AltosUIMapCache cache = new AltosUIMapCache();
+
+ AltosUIMapPos lat;
+ AltosUIMapPos lon;
+
+ JProgressBar pbar;
+ int pbar_max;
+ int pbar_cur;
+
+ AltosUISites sites;
+ JLabel site_list_label;
+ JComboBox<AltosUISite> site_list;
+
+ JToggleButton load_button;
+ boolean loading;
+ JButton close_button;
+
+ JCheckBox[] maptypes = new JCheckBox[AltosUIMap.maptype_terrain - AltosUIMap.maptype_hybrid + 1];
+
+ JComboBox<Integer> min_zoom;
+ JComboBox<Integer> max_zoom;
+ JComboBox<Integer> radius;
+
+ Integer[] zooms = { -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 };
+ Integer[] radii = { 1, 2, 3, 4, 5 };
+
+ static final String[] lat_hemi_names = { "N", "S" };
+ static final String[] lon_hemi_names = { "E", "W" };
+
+ class updatePbar implements Runnable {
+ String s;
+
+ public updatePbar(String in_s) {
+ s = in_s;
+ }
+
+ public void run() {
+ int n = ++pbar_cur;
+
+ pbar.setMaximum(pbar_max);
+ pbar.setValue(n);
+ pbar.setString(s);
+ }
+ }
+
+ double latitude, longitude;
+ int min_z;
+ int max_z;
+ int cur_z;
+ int all_types;
+ int cur_type;
+ int r;
+
+ int tiles_per_layer;
+ int tiles_loaded;
+ int layers_total;
+ int layers_loaded;
+
+
+ private void do_load() {
+ tiles_loaded = 0;
+ map.set_zoom(cur_z + AltosUIMapView.default_zoom);
+ map.set_maptype(cur_type);
+ map.set_load_params(latitude, longitude, r, this);
+ }
+
+ private int next_type(int start) {
+ int next_type;
+ for (next_type = start;
+ next_type <= AltosUIMap.maptype_terrain && (all_types & (1 << next_type)) == 0;
+ next_type++)
+ ;
+ return next_type;
+ }
+
+ private void next_load() {
+ int next_type = next_type(cur_type + 1);
+
+ if (next_type > AltosUIMap.maptype_terrain) {
+ if (cur_z == max_z) {
+ return;
+ } else {
+ cur_z++;
+ }
+ next_type = next_type(0);
+ }
+ cur_type = next_type;
+ do_load();
+ }
+
+ private void start_load() {
+ cur_z = min_z;
+ int ntype = 0;
+ all_types = 0;
+ for (int t = AltosUIMap.maptype_hybrid; t <= AltosUIMap.maptype_terrain; t++)
+ if (maptypes[t].isSelected()) {
+ all_types |= (1 << t);
+ ntype++;
+ }
+ if (ntype == 0) {
+ all_types |= (1 << AltosUIMap.maptype_hybrid);
+ ntype = 1;
+ }
+
+ cur_type = next_type(0);
+ tiles_per_layer = (r * 2 + 1) * (r * 2 + 1);
+ layers_total = (max_z - min_z + 1) * ntype;
+ layers_loaded = 0;
+ pbar_max = layers_total * tiles_per_layer;
+ pbar_cur = 0;
+
+ map.clear_marks();
+ map.add_mark(latitude,longitude, AltosLib.ao_flight_boost);
+ do_load();
+ }
+
+ /* AltosUIMapTileListener methods */
+
+ public synchronized void notify_tile(AltosUIMapTile tile, int status) {
+ if (status == AltosUIMapStore.loading)
+ return;
+
+ SwingUtilities.invokeLater(new updatePbar(tile.store.file.toString()));
+ ++tiles_loaded;
+ if (tiles_loaded == tiles_per_layer) {
+ ++layers_loaded;
+ if (layers_loaded == layers_total) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ pbar.setValue(0);
+ pbar.setString("");
+ load_button.setSelected(false);
+ loading = false;
+ }
+ });
+ } else {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ next_load();
+ }
+ });
+ }
+ }
+ }
+
+ public AltosUIMapCache cache() { return cache; }
+
+ public void set_sites() {
+ int i = 1;
+ for (AltosUISite site : sites.sites) {
+ site_list.insertItemAt(site, i);
+ i++;
+ }
+ }
+
+ public void itemStateChanged(ItemEvent e) {
+ int state = e.getStateChange();
+
+ if (state == ItemEvent.SELECTED) {
+ Object o = e.getItem();
+ if (o instanceof AltosUISite) {
+ AltosUISite site = (AltosUISite) o;
+ lat.set_value(site.latitude);
+ lon.set_value(site.longitude);
+ }
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ String cmd = e.getActionCommand();
+
+ if (cmd.equals("close"))
+ setVisible(false);
+
+ if (cmd.equals("load")) {
+ if (!loading) {
+ try {
+ latitude = lat.get_value();
+ longitude = lon.get_value();
+ min_z = (Integer) min_zoom.getSelectedItem();
+ max_z = (Integer) max_zoom.getSelectedItem();
+ if (max_z < min_z)
+ max_z = min_z;
+ r = (Integer) radius.getSelectedItem();
+ loading = true;
+ } catch (NumberFormatException ne) {
+ load_button.setSelected(false);
+ }
+ start_load();
+ }
+ }
+ }
+
+ public AltosUIMapPreload(AltosUIFrame in_owner) {
+ owner = in_owner;
+
+ Container pane = getContentPane();
+ GridBagConstraints c = new GridBagConstraints();
+ Insets i = new Insets(4,4,4,4);
+
+ setTitle("AltOS Load Maps");
+
+ pane.setLayout(new GridBagLayout());
+
+ map = new AltosUIMap();
+
+ c.fill = GridBagConstraints.BOTH;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 1;
+
+ c.gridx = 0;
+ c.gridy = 0;
+ c.gridwidth = 10;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(map, c);
+
+ pbar = new JProgressBar();
+ pbar.setMinimum(0);
+ pbar.setMaximum(1);
+ pbar.setValue(0);
+ pbar.setString("");
+ pbar.setStringPainted(true);
+
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 0;
+ c.gridy = 1;
+ c.gridwidth = 10;
+
+ pane.add(pbar, c);
+
+ site_list_label = new JLabel ("Known Launch Sites:");
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 0;
+ c.gridy = 2;
+ c.gridwidth = 1;
+
+ pane.add(site_list_label, c);
+
+ site_list = new JComboBox<AltosUISite>(new AltosUISite[] { new AltosUISite("Site List", 0, 0) });
+ site_list.addItemListener(this);
+
+ sites = new AltosUISites(this);
+
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 1;
+ c.gridy = 2;
+ c.gridwidth = 1;
+
+ pane.add(site_list, c);
+
+ lat = new AltosUIMapPos(owner,
+ "Latitude:",
+ lat_hemi_names,
+ 37.167833333);
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 0;
+ c.weighty = 0;
+
+ c.gridx = 0;
+ c.gridy = 3;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(lat, c);
+
+ lon = new AltosUIMapPos(owner,
+ "Longitude:",
+ lon_hemi_names,
+ -97.73975);
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 0;
+ c.weighty = 0;
+
+ c.gridx = 1;
+ c.gridy = 3;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(lon, c);
+
+ load_button = new JToggleButton("Load Map");
+ load_button.addActionListener(this);
+ load_button.setActionCommand("load");
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 0;
+ c.gridy = 4;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(load_button, c);
+
+ close_button = new JButton("Close");
+ close_button.addActionListener(this);
+ close_button.setActionCommand("close");
+
+ c.fill = GridBagConstraints.NONE;
+ c.anchor = GridBagConstraints.CENTER;
+ c.insets = i;
+ c.weightx = 1;
+ c.weighty = 0;
+
+ c.gridx = 1;
+ c.gridy = 4;
+ c.gridwidth = 1;
+ c.anchor = GridBagConstraints.CENTER;
+
+ pane.add(close_button, c);
+
+ JLabel types_label = new JLabel("Map Types");
+ c.gridx = 2;
+ c.gridwidth = 2;
+ c.gridy = 2;
+ pane.add(types_label, c);
+
+ c.gridwidth = 1;
+
+ for (int type = AltosUIMap.maptype_hybrid; type <= AltosUIMap.maptype_terrain; type++) {
+ maptypes[type] = new JCheckBox(AltosUIMap.maptype_labels[type],
+ type == AltosUIMap.maptype_hybrid);
+ c.gridx = 2 + (type >> 1);
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridy = (type & 1) + 3;
+ pane.add(maptypes[type], c);
+ }
+
+ JLabel min_zoom_label = new JLabel("Minimum Zoom");
+ c.gridx = 4;
+ c.gridy = 2;
+ pane.add(min_zoom_label, c);
+
+ min_zoom = new JComboBox<Integer>(zooms);
+ min_zoom.setSelectedItem(zooms[10]);
+ min_zoom.setEditable(false);
+ c.gridx = 5;
+ c.gridy = 2;
+ pane.add(min_zoom, c);
+
+ JLabel max_zoom_label = new JLabel("Maximum Zoom");
+ c.gridx = 4;
+ c.gridy = 3;
+ pane.add(max_zoom_label, c);
+
+ max_zoom = new JComboBox<Integer>(zooms);
+ max_zoom.setSelectedItem(zooms[14]);
+ max_zoom.setEditable(false);
+ c.gridx = 5;
+ c.gridy = 3;
+ pane.add(max_zoom, c);
+
+ JLabel radius_label = new JLabel("Tile Radius");
+ c.gridx = 4;
+ c.gridy = 4;
+ pane.add(radius_label, c);
+
+ radius = new JComboBox<Integer>(radii);
+ radius.setSelectedItem(radii[4]);
+ radius.setEditable(true);
+ c.gridx = 5;
+ c.gridy = 4;
+ pane.add(radius, c);
+
+ pack();
+ setLocationRelativeTo(owner);
+ setVisible(true);
+ }
+}
diff --git a/altosuilib/AltosUIMapRectangle.java b/altosuilib/AltosUIMapRectangle.java
new file mode 100644
index 00000000..8a5b16e1
--- /dev/null
+++ b/altosuilib/AltosUIMapRectangle.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+public class AltosUIMapRectangle {
+ AltosUILatLon ul, lr;
+
+ public AltosUIMapRectangle(AltosUILatLon a, AltosUILatLon b) {
+ double ul_lat, ul_lon;
+ double lr_lat, lr_lon;
+
+ if (a.lat > b.lat) {
+ ul_lat = a.lat;
+ lr_lat = b.lat;
+ } else {
+ ul_lat = b.lat;
+ lr_lat = a.lat;
+ }
+ if (a.lon < b.lon) {
+ ul_lon = a.lon;
+ lr_lon = b.lon;
+ } else {
+ ul_lon = b.lon;
+ lr_lon = a.lon;
+ }
+
+ ul = new AltosUILatLon(ul_lat, ul_lon);
+ lr = new AltosUILatLon(lr_lat, lr_lon);
+ }
+}
diff --git a/altosuilib/AltosUIMapStore.java b/altosuilib/AltosUIMapStore.java
new file mode 100644
index 00000000..4cecb54f
--- /dev/null
+++ b/altosuilib/AltosUIMapStore.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+public class AltosUIMapStore {
+ String url;
+ File file;
+ LinkedList<AltosUIMapStoreListener> listeners = new LinkedList<AltosUIMapStoreListener>();
+
+ static final int success = 0;
+ static final int loading = 1;
+ static final int failed = 2;
+ static final int bad_request = 3;
+ static final int forbidden = 4;
+
+ int status;
+
+ public int status() {
+ return status;
+ }
+
+ public synchronized void add_listener(AltosUIMapStoreListener listener) {
+ if (!listeners.contains(listener))
+ listeners.add(listener);
+ }
+
+ public synchronized void remove_listener(AltosUIMapStoreListener listener) {
+ listeners.remove(listener);
+ }
+
+ private synchronized void notify_listeners(int status) {
+ this.status = status;
+ for (AltosUIMapStoreListener listener : listeners)
+ listener.notify_store(this, status);
+ }
+
+ static Object forbidden_lock = new Object();
+ static long forbidden_time;
+ static boolean forbidden_set;
+
+ private int fetch_url() {
+ URL u;
+
+ try {
+ u = new URL(url);
+ } catch (java.net.MalformedURLException e) {
+ return bad_request;
+ }
+
+ byte[] data;
+ URLConnection uc = null;
+ try {
+ uc = u.openConnection();
+ String type = uc.getContentType();
+ int contentLength = uc.getContentLength();
+ if (uc instanceof HttpURLConnection) {
+ int response = ((HttpURLConnection) uc).getResponseCode();
+ switch (response) {
+ case HttpURLConnection.HTTP_FORBIDDEN:
+ case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
+ case HttpURLConnection.HTTP_UNAUTHORIZED:
+ synchronized (forbidden_lock) {
+ forbidden_time = System.nanoTime();
+ forbidden_set = true;
+ return forbidden;
+ }
+ }
+ }
+ InputStream in = new BufferedInputStream(uc.getInputStream());
+ int bytesRead = 0;
+ int offset = 0;
+ data = new byte[contentLength];
+ while (offset < contentLength) {
+ bytesRead = in.read(data, offset, data.length - offset);
+ if (bytesRead == -1)
+ break;
+ offset += bytesRead;
+ }
+ in.close();
+
+ if (offset != contentLength)
+ return failed;
+
+ } catch (IOException e) {
+ return failed;
+ }
+
+ try {
+ FileOutputStream out = new FileOutputStream(file);
+ out.write(data);
+ out.flush();
+ out.close();
+ } catch (FileNotFoundException e) {
+ return bad_request;
+ } catch (IOException e) {
+ if (file.exists())
+ file.delete();
+ return bad_request;
+ }
+ return success;
+ }
+
+ static Object fetch_lock = new Object();
+
+ static final long forbidden_interval = 60l * 1000l * 1000l * 1000l;
+ static final long google_maps_ratelimit_ms = 1200;
+
+ class loader implements Runnable {
+
+ public void run() {
+ if (file.exists()) {
+ notify_listeners(success);
+ return;
+ }
+
+ synchronized(forbidden_lock) {
+ if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
+ notify_listeners(forbidden);
+ return;
+ }
+ }
+
+ int new_status;
+
+ if (!AltosUIVersion.has_google_maps_api_key()) {
+ synchronized (fetch_lock) {
+ long startTime = System.nanoTime();
+ new_status = fetch_url();
+ if (new_status == success) {
+ long duration_ms = (System.nanoTime() - startTime) / 1000000;
+ if (duration_ms < google_maps_ratelimit_ms) {
+ try {
+ Thread.sleep(google_maps_ratelimit_ms - duration_ms);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+ } else {
+ new_status = fetch_url();
+ }
+ notify_listeners(new_status);
+ }
+ }
+
+ private void load() {
+ loader l = new loader();
+ Thread lt = new Thread(l);
+ lt.start();
+ }
+
+ private AltosUIMapStore (String url, File file) {
+ this.url = url;
+ this.file = file;
+
+ if (file.exists())
+ status = success;
+ else {
+ status = loading;
+ load();
+ }
+ }
+
+ public boolean equals(AltosUIMapStore other) {
+ return url.equals(other.url);
+ }
+
+ static HashMap<String,AltosUIMapStore> stores = new HashMap<String,AltosUIMapStore>();
+
+ public static AltosUIMapStore get(String url, File file) {
+ AltosUIMapStore store;
+ synchronized(stores) {
+ if (stores.containsKey(url)) {
+ store = stores.get(url);
+ } else {
+ store = new AltosUIMapStore(url, file);
+ stores.put(url, store);
+ }
+ }
+ return store;
+ }
+
+}
diff --git a/altosuilib/AltosUIMapStoreListener.java b/altosuilib/AltosUIMapStoreListener.java
new file mode 100644
index 00000000..91aff00c
--- /dev/null
+++ b/altosuilib/AltosUIMapStoreListener.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+public interface AltosUIMapStoreListener {
+ abstract void notify_store(AltosUIMapStore store, int status);
+}
diff --git a/altosuilib/AltosUIMapTile.java b/altosuilib/AltosUIMapTile.java
new file mode 100644
index 00000000..7c823183
--- /dev/null
+++ b/altosuilib/AltosUIMapTile.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.image.*;
+import javax.swing.*;
+import javax.imageio.*;
+import java.awt.geom.*;
+import java.io.*;
+import java.util.*;
+import java.awt.RenderingHints.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosUIMapTile {
+ AltosUIMapTileListener listener;
+ AltosUILatLon upper_left, center;
+ int px_size;
+ int zoom;
+ int maptype;
+ AltosUIMapStore store;
+ AltosUIMapCache cache;
+ int status;
+
+ private File map_file() {
+ double lat = center.lat;
+ double lon = center.lon;
+ char chlat = lat < 0 ? 'S' : 'N';
+ char chlon = lon < 0 ? 'W' : 'E';
+
+ if (lat < 0) lat = -lat;
+ if (lon < 0) lon = -lon;
+ String maptype_string = String.format("%s-", AltosUIMap.maptype_names[maptype]);
+ String format_string;
+ if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain)
+ format_string = "jpg";
+ else
+ format_string = "png";
+ return new File(AltosUIPreferences.mapdir(),
+ String.format("map-%c%.6f,%c%.6f-%s%d.%s",
+ chlat, lat, chlon, lon, maptype_string, zoom, format_string));
+ }
+
+ private String map_url() {
+ String format_string;
+ if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain)
+ format_string = "jpg";
+ else
+ format_string = "png32";
+
+ if (AltosUIVersion.has_google_maps_api_key())
+ return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s&key=%s",
+ center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string, AltosUIVersion.google_maps_api_key);
+ else
+ return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s",
+ center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string);
+ }
+ private Font font = null;
+
+ public void set_font(Font font) {
+ this.font = font;
+ }
+
+ int painting_serial;
+ int painted_serial;
+
+ Image image;
+
+ public void paint_graphics(Graphics2D g2d, AltosUIMapTransform t, int serial) {
+ if (serial < painted_serial)
+ return;
+
+ Point2D.Double point_double = t.screen(upper_left);
+ Point point = new Point((int) (point_double.x + 0.5),
+ (int) (point_double.y + 0.5));
+
+ painted_serial = serial;
+
+ if (!g2d.hitClip(point.x, point.y, px_size, px_size))
+ return;
+
+ if (image != null) {
+ g2d.drawImage(image, point.x, point.y, null);
+ image = null;
+ } else {
+ g2d.setColor(Color.GRAY);
+ g2d.fillRect(point.x, point.y, px_size, px_size);
+
+ if (t.has_location()) {
+ String message = null;
+ switch (status) {
+ case AltosUIMapCache.loading:
+ message = "Loading...";
+ break;
+ case AltosUIMapCache.bad_request:
+ message = "Internal error";
+ break;
+ case AltosUIMapCache.failed:
+ message = "Network error, check connection";
+ break;
+ case AltosUIMapCache.forbidden:
+ message = "Too many requests, try later";
+ break;
+ }
+ if (message != null && font != null) {
+ g2d.setFont(font);
+ g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ Rectangle2D bounds = font.getStringBounds(message, g2d.getFontRenderContext());
+
+ float x = px_size / 2.0f;
+ float y = px_size / 2.0f;
+ x = x - (float) bounds.getWidth() / 2.0f;
+ y = y + (float) bounds.getHeight() / 2.0f;
+ g2d.setColor(Color.BLACK);
+ g2d.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
+ }
+ }
+ }
+ }
+
+ public void set_status(int status) {
+ this.status = status;
+ listener.notify_tile(this, status);
+ }
+
+ public void notify_image(Image image) {
+ listener.notify_tile(this, status);
+ }
+
+ public void paint(Graphics g, AltosUIMapTransform t) {
+ Graphics2D g2d = (Graphics2D) g;
+ boolean queued = false;
+
+ Point2D.Double point = t.screen(upper_left);
+
+ if (!g.hitClip((int) (point.x + 0.5), (int) (point.y + 0.5), px_size, px_size))
+ return;
+
+ ++painting_serial;
+
+ if (image == null && t.has_location())
+ image = cache.get(this, store, px_size, px_size);
+
+ paint_graphics(g2d, t, painting_serial);
+ }
+
+ public int store_status() {
+ return store.status();
+ }
+
+ public void add_store_listener(AltosUIMapStoreListener listener) {
+ store.add_listener(listener);
+ }
+
+ public void remove_store_listener(AltosUIMapStoreListener listener) {
+ store.remove_listener(listener);
+ }
+
+ public AltosUIMapTile(AltosUIMapTileListener listener, AltosUILatLon upper_left, AltosUILatLon center, int zoom, int maptype, int px_size, Font font) {
+ this.listener = listener;
+ this.upper_left = upper_left;
+ cache = listener.cache();
+
+ while (center.lon < -180.0)
+ center.lon += 360.0;
+ while (center.lon > 180.0)
+ center.lon -= 360.0;
+
+ this.center = center;
+ this.zoom = zoom;
+ this.maptype = maptype;
+ this.px_size = px_size;
+ this.font = font;
+ status = AltosUIMapCache.loading;
+ store = AltosUIMapStore.get(map_url(), map_file());
+ }
+}
diff --git a/altosuilib/AltosUIMapTileListener.java b/altosuilib/AltosUIMapTileListener.java
new file mode 100644
index 00000000..4ca13539
--- /dev/null
+++ b/altosuilib/AltosUIMapTileListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+public interface AltosUIMapTileListener {
+ abstract public void notify_tile(AltosUIMapTile tile, int status);
+
+ abstract public AltosUIMapCache cache();
+}
diff --git a/altosuilib/AltosUIMapTransform.java b/altosuilib/AltosUIMapTransform.java
new file mode 100644
index 00000000..e6f1ffe3
--- /dev/null
+++ b/altosuilib/AltosUIMapTransform.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.Math;
+import java.awt.geom.*;
+import java.util.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosUIMapTransform {
+
+ double scale_x, scale_y;
+
+ double offset_x, offset_y;
+
+ public AltosUILatLon lat_lon (Point2D.Double point) {
+ double lat, lon;
+ double rads;
+
+ lon = point.x/scale_x;
+ rads = 2 * Math.atan(Math.exp(-point.y/scale_y));
+ lat = Math.toDegrees(rads - Math.PI/2);
+
+ return new AltosUILatLon(lat,lon);
+ }
+
+ public Point2D.Double screen_point(Point screen) {
+ return new Point2D.Double(screen.x + offset_x, screen.y + offset_y);
+ }
+
+ public AltosUILatLon screen_lat_lon(Point screen) {
+ return lat_lon(screen_point(screen));
+ }
+
+ public Point2D.Double point(AltosUILatLon lat_lon) {
+ double x, y;
+ double e;
+
+ x = lat_lon.lon * scale_x;
+
+ e = Math.sin(Math.toRadians(lat_lon.lat));
+ e = Math.max(e,-(1-1.0E-15));
+ e = Math.min(e, 1-1.0E-15 );
+
+ y = 0.5*Math.log((1+e)/(1-e))*-scale_y;
+
+ return new Point2D.Double(x, y);
+ }
+
+ public Point2D.Double screen(Point2D.Double point) {
+ return new Point2D.Double(point.x - offset_x, point.y - offset_y);
+ }
+
+ public Point screen(Point point) {
+ return new Point((int) (point.x - offset_x + 0.5),
+ (int) (point.y - offset_y + 0.5));
+ }
+
+ public Rectangle screen(AltosUIMapRectangle map_rect) {
+ Point2D.Double ul = screen(map_rect.ul);
+ Point2D.Double lr = screen(map_rect.lr);
+
+ return new Rectangle((int) ul.x, (int) ul.y, (int) (lr.x - ul.x), (int) (lr.y - ul.y));
+ }
+
+ public Point2D.Double screen(AltosUILatLon lat_lon) {
+ return screen(point(lat_lon));
+ }
+
+ private boolean has_location;
+
+ public boolean has_location() {
+ return has_location;
+ }
+
+ public AltosUIMapTransform(int width, int height, int zoom, AltosUILatLon centre_lat_lon) {
+ scale_x = 256/360.0 * Math.pow(2, zoom);
+ scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
+
+ Point2D.Double centre_pt = point(centre_lat_lon);
+
+ has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0);
+ offset_x = centre_pt.x - width / 2.0;
+ offset_y = centre_pt.y - height / 2.0;
+ }
+}
diff --git a/altosuilib/AltosUIMapView.java b/altosuilib/AltosUIMapView.java
new file mode 100644
index 00000000..a14fde65
--- /dev/null
+++ b/altosuilib/AltosUIMapView.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.*;
+import java.awt.geom.*;
+import java.util.*;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosUIMapView extends Component implements MouseMotionListener, MouseListener, MouseWheelListener, ComponentListener, AltosUIMapTileListener, AltosUIMapStoreListener {
+
+ AltosUIMapPath path = new AltosUIMapPath();
+
+ AltosUIMapLine line = new AltosUIMapLine();
+
+ AltosUIMapCache cache = new AltosUIMapCache();
+
+ LinkedList<AltosUIMapMark> marks = new LinkedList<AltosUIMapMark>();
+
+ LinkedList<AltosUIMapZoomListener> zoom_listeners = new LinkedList<AltosUIMapZoomListener>();
+
+ boolean have_boost = false;
+ boolean have_landed = false;
+
+ ConcurrentHashMap<Point,AltosUIMapTile> tiles = new ConcurrentHashMap<Point,AltosUIMapTile>();
+
+ static final int default_zoom = 15;
+ static final int min_zoom = 3;
+ static final int max_zoom = 21;
+ static final int px_size = 512;
+
+ int load_radius;
+ AltosUILatLon load_centre = null;
+ AltosUIMapTileListener load_listener;
+
+ int zoom = default_zoom;
+ int maptype = AltosUIMap.maptype_default;
+
+ long user_input_time;
+
+ /* Milliseconds to wait after user action before auto-scrolling
+ */
+ static final long auto_scroll_delay = 20 * 1000;
+
+ AltosUIMapTransform transform;
+ AltosUILatLon centre;
+
+ public void set_font() {
+ line.set_font(AltosUILib.value_font);
+ for (AltosUIMapTile tile : tiles.values())
+ tile.set_font(AltosUILib.value_font);
+ repaint();
+ }
+
+ public void set_units() {
+ repaint();
+ }
+
+ private boolean is_drag_event(MouseEvent e) {
+ return e.getModifiers() == InputEvent.BUTTON1_MASK;
+ }
+
+ Point drag_start;
+
+ private void drag(MouseEvent e) {
+ if (drag_start == null)
+ return;
+
+ int dx = e.getPoint().x - drag_start.x;
+ int dy = e.getPoint().y - drag_start.y;
+
+ AltosUILatLon new_centre = transform.screen_lat_lon(new Point(getWidth() / 2 - dx, getHeight() / 2 - dy));
+ centre (new_centre.lat, new_centre.lon);
+ drag_start = e.getPoint();
+ }
+
+ private void drag_start(MouseEvent e) {
+ drag_start = e.getPoint();
+ }
+
+ private void notice_user_input() {
+ user_input_time = System.currentTimeMillis();
+ }
+
+ private boolean recent_user_input() {
+ return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay;
+ }
+
+ /* MouseMotionListener methods */
+
+ public void mouseDragged(MouseEvent e) {
+ notice_user_input();
+ if (is_drag_event(e))
+ drag(e);
+ else {
+ line.dragged(e, transform);
+ repaint();
+ }
+ }
+
+ public void mouseMoved(MouseEvent e) {
+ }
+
+ /* MouseListener methods */
+ public void mouseClicked(MouseEvent e) {
+ }
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+ public void mousePressed(MouseEvent e) {
+ notice_user_input();
+ if (is_drag_event(e))
+ drag_start(e);
+ else {
+ line.pressed(e, transform);
+ repaint();
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ }
+
+ /* MouseWheelListener methods */
+
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ int zoom_change = e.getWheelRotation();
+
+ notice_user_input();
+ AltosUILatLon mouse_lat_lon = transform.screen_lat_lon(e.getPoint());
+ set_zoom(zoom() - zoom_change);
+
+ Point2D.Double new_mouse = transform.screen(mouse_lat_lon);
+
+ int dx = getWidth()/2 - e.getPoint().x;
+ int dy = getHeight()/2 - e.getPoint().y;
+
+ AltosUILatLon new_centre = transform.screen_lat_lon(new Point((int) new_mouse.x + dx, (int) new_mouse.y + dy));
+
+ centre(new_centre.lat, new_centre.lon);
+ }
+
+ /* ComponentListener methods */
+
+ public void componentHidden(ComponentEvent e) {
+ }
+
+ public void componentMoved(ComponentEvent e) {
+ }
+
+ public void componentResized(ComponentEvent e) {
+ set_transform();
+ }
+
+ public void componentShown(ComponentEvent e) {
+ set_transform();
+ }
+
+ public void repaint(Rectangle r, int pad) {
+ repaint(r.x - pad, r.y - pad, r.width + pad*2, r.height + pad*2);
+ }
+
+ public void repaint(AltosUIMapRectangle rect, int pad) {
+ repaint (transform.screen(rect), pad);
+ }
+
+ private boolean far_from_centre(AltosUILatLon lat_lon) {
+
+ if (centre == null || transform == null)
+ return true;
+
+ Point2D.Double screen = transform.screen(lat_lon);
+
+ int width = getWidth();
+ int dx = Math.abs ((int) screen.x - width/2);
+
+ if (dx > width / 4)
+ return true;
+
+ int height = getHeight();
+ int dy = Math.abs ((int) screen.y - height/2);
+
+ if (dy > height / 4)
+ return true;
+
+ return false;
+ }
+
+ public void show(AltosState state, AltosListenerState listener_state) {
+
+ /* If insufficient gps data, nothing to update
+ */
+ AltosGPS gps = state.gps;
+
+ if (gps == null)
+ return;
+
+ if (!gps.locked && gps.nsat < 4)
+ return;
+
+ AltosUIMapRectangle damage = path.add(gps.lat, gps.lon, state.state);
+
+ switch (state.state) {
+ case AltosLib.ao_flight_boost:
+ if (!have_boost) {
+ add_mark(gps.lat, gps.lon, state.state);
+ have_boost = true;
+ }
+ break;
+ case AltosLib.ao_flight_landed:
+ if (!have_landed) {
+ add_mark(gps.lat, gps.lon, state.state);
+ have_landed = true;
+ }
+ break;
+ }
+
+ if (damage != null)
+ repaint(damage, AltosUIMapPath.stroke_width);
+ maybe_centre(gps.lat, gps.lon);
+ }
+
+ private void set_transform() {
+ Rectangle bounds = getBounds();
+
+ transform = new AltosUIMapTransform(bounds.width, bounds.height, zoom, centre);
+ repaint();
+ }
+
+ public boolean set_zoom(int zoom) {
+ if (min_zoom <= zoom && zoom <= max_zoom && zoom != this.zoom) {
+ this.zoom = zoom;
+ tiles.clear();
+ set_transform();
+
+ for (AltosUIMapZoomListener listener : zoom_listeners)
+ listener.zoom_changed(this.zoom);
+
+ return true;
+ }
+ return false;
+ }
+
+ public void add_zoom_listener(AltosUIMapZoomListener listener) {
+ if (!zoom_listeners.contains(listener))
+ zoom_listeners.add(listener);
+ }
+
+ public void remove_zoom_listener(AltosUIMapZoomListener listener) {
+ zoom_listeners.remove(listener);
+ }
+
+ public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) {
+ load_centre = new AltosUILatLon(lat, lon);
+ load_radius = radius;
+ load_listener = listener;
+ centre(lat, lon);
+ make_tiles();
+ for (AltosUIMapTile tile : tiles.values()) {
+ tile.add_store_listener(this);
+ if (tile.store_status() != AltosUIMapStore.loading)
+ listener.notify_tile(tile, tile.store_status());
+ }
+ repaint();
+ }
+
+ public boolean all_fetched() {
+ for (AltosUIMapTile tile : tiles.values()) {
+ if (tile.store_status() == AltosUIMapStore.loading)
+ return false;
+ }
+ return true;
+ }
+
+ public boolean set_maptype(int maptype) {
+ if (maptype != this.maptype) {
+ this.maptype = maptype;
+ tiles.clear();
+ repaint();
+ return true;
+ }
+ return false;
+ }
+
+ public int get_maptype() {
+ return maptype;
+ }
+
+ public int zoom() {
+ return zoom;
+ }
+
+ public void centre(AltosUILatLon lat_lon) {
+ centre = lat_lon;
+ set_transform();
+ }
+
+ public void centre(double lat, double lon) {
+ centre(new AltosUILatLon(lat, lon));
+ }
+
+ public void maybe_centre(double lat, double lon) {
+ AltosUILatLon lat_lon = new AltosUILatLon(lat, lon);
+ if (centre == null || (!recent_user_input() && far_from_centre(lat_lon)))
+ centre(lat_lon);
+ }
+
+ private VolatileImage create_back_buffer() {
+ return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight());
+ }
+
+ private Point floor(Point2D.Double point) {
+ return new Point ((int) Math.floor(point.x / px_size) * px_size,
+ (int) Math.floor(point.y / px_size) * px_size);
+ }
+
+ private Point ceil(Point2D.Double point) {
+ return new Point ((int) Math.ceil(point.x / px_size) * px_size,
+ (int) Math.ceil(point.y / px_size) * px_size);
+ }
+
+ private void make_tiles() {
+ Point upper_left;
+ Point lower_right;
+
+ if (load_centre != null) {
+ Point centre = floor(transform.point(load_centre));
+
+ upper_left = new Point(centre.x - load_radius * px_size,
+ centre.y - load_radius * px_size);
+ lower_right = new Point(centre.x + load_radius * px_size,
+ centre.y + load_radius * px_size);
+ } else {
+ upper_left = floor(transform.screen_point(new Point(0, 0)));
+ lower_right = floor(transform.screen_point(new Point(getWidth(), getHeight())));
+ }
+ LinkedList<Point> to_remove = new LinkedList<Point>();
+
+ for (Point point : tiles.keySet()) {
+ if (point.x < upper_left.x || lower_right.x < point.x ||
+ point.y < upper_left.y || lower_right.y < point.y) {
+ to_remove.add(point);
+ }
+ }
+
+ for (Point point : to_remove)
+ tiles.remove(point);
+
+ cache.set_cache_size(((lower_right.y - upper_left.y) / px_size + 1) * ((lower_right.x - upper_left.x) / px_size + 1));
+ for (int y = upper_left.y; y <= lower_right.y; y += px_size) {
+ for (int x = upper_left.x; x <= lower_right.x; x += px_size) {
+ Point point = new Point(x, y);
+
+ if (!tiles.containsKey(point)) {
+ AltosUILatLon ul = transform.lat_lon(new Point2D.Double(x, y));
+ AltosUILatLon center = transform.lat_lon(new Point2D.Double(x + px_size/2, y + px_size/2));
+ AltosUIMapTile tile = new AltosUIMapTile(this, ul, center, zoom, maptype,
+ px_size, AltosUILib.value_font);
+ tiles.put(point, tile);
+ }
+ }
+ }
+ }
+
+ /* AltosUIMapTileListener methods */
+ public synchronized void notify_tile(AltosUIMapTile tile, int status) {
+ for (Point point : tiles.keySet()) {
+ if (tile == tiles.get(point)) {
+ Point screen = transform.screen(point);
+ repaint(screen.x, screen.y, px_size, px_size);
+ }
+ }
+ }
+
+ public AltosUIMapCache cache() { return cache; }
+
+ /* AltosUIMapStoreListener methods */
+ public synchronized void notify_store(AltosUIMapStore store, int status) {
+ if (load_listener != null) {
+ for (AltosUIMapTile tile : tiles.values())
+ if (store.equals(tile.store))
+ load_listener.notify_tile(tile, status);
+ }
+ }
+
+ private void do_paint(Graphics g) {
+ Graphics2D g2d = (Graphics2D) g;
+
+ make_tiles();
+
+ for (AltosUIMapTile tile : tiles.values())
+ tile.paint(g2d, transform);
+
+ synchronized(marks) {
+ for (AltosUIMapMark mark : marks)
+ mark.paint(g2d, transform);
+ }
+
+ path.paint(g2d, transform);
+
+ line.paint(g2d, transform);
+ }
+
+ public void paint(Graphics g) {
+ VolatileImage back_buffer = create_back_buffer();
+ do {
+ GraphicsConfiguration gc = getGraphicsConfiguration();
+ int code = back_buffer.validate(gc);
+ if (code == VolatileImage.IMAGE_INCOMPATIBLE)
+ back_buffer = create_back_buffer();
+
+ Graphics g_back = back_buffer.getGraphics();
+ g_back.setClip(g.getClip());
+ do_paint(g_back);
+ g_back.dispose();
+
+ g.drawImage(back_buffer, 0, 0, this);
+ } while (back_buffer.contentsLost());
+ back_buffer.flush();
+ }
+
+ public void update(Graphics g) {
+ paint(g);
+ }
+
+ public void add_mark(double lat, double lon, int state) {
+ synchronized(marks) {
+ marks.add(new AltosUIMapMark(lat, lon, state));
+ }
+ repaint();
+ }
+
+ public void clear_marks() {
+ synchronized(marks) {
+ marks.clear();
+ }
+ }
+
+ public AltosUIMapView() {
+ centre(0, 0);
+
+ addComponentListener(this);
+ addMouseMotionListener(this);
+ addMouseListener(this);
+ addMouseWheelListener(this);
+ set_font();
+ }
+}
diff --git a/altosuilib/AltosUIMapZoomListener.java b/altosuilib/AltosUIMapZoomListener.java
new file mode 100644
index 00000000..02e8bb51
--- /dev/null
+++ b/altosuilib/AltosUIMapZoomListener.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+public interface AltosUIMapZoomListener {
+ abstract public void zoom_changed(int zoom);
+}
diff --git a/altosuilib/AltosUIMarker.java b/altosuilib/AltosUIMarker.java
index ae8eb034..cd6fa589 100644
--- a/altosuilib/AltosUIMarker.java
+++ b/altosuilib/AltosUIMarker.java
@@ -15,14 +15,14 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_3.*;
+import org.altusmetrum.altoslib_4.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
@@ -41,7 +41,7 @@ public class AltosUIMarker implements AltosUIGrapher {
boolean enabled;
int fetch;
Color color;
-
+
private void remove_markers() {
for (ValueMarker marker : markers)
plot.removeDomainMarker(marker);
diff --git a/altosuilib/AltosUIPreferences.java b/altosuilib/AltosUIPreferences.java
index 4c995f80..7a582a7d 100644
--- a/altosuilib/AltosUIPreferences.java
+++ b/altosuilib/AltosUIPreferences.java
@@ -15,13 +15,13 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.io.*;
import java.util.*;
import java.awt.Component;
import javax.swing.*;
-import org.altusmetrum.altoslib_3.*;
+import org.altusmetrum.altoslib_4.*;
public class AltosUIPreferences extends AltosPreferences {
diff --git a/altosuilib/AltosUIPreferencesBackend.java b/altosuilib/AltosUIPreferencesBackend.java
index 64d3e3df..da29253d 100644
--- a/altosuilib/AltosUIPreferencesBackend.java
+++ b/altosuilib/AltosUIPreferencesBackend.java
@@ -15,17 +15,17 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.io.File;
import java.util.prefs.*;
-import org.altusmetrum.altoslib_3.*;
+import org.altusmetrum.altoslib_4.*;
import javax.swing.filechooser.FileSystemView;
public class AltosUIPreferencesBackend implements AltosPreferencesBackend {
private Preferences _preferences = null;
-
+
public AltosUIPreferencesBackend() {
_preferences = Preferences.userRoot().node("/org/altusmetrum/altosui");
}
diff --git a/altosuilib/AltosUISeries.java b/altosuilib/AltosUISeries.java
index 1f2a1c3f..b0632d18 100644
--- a/altosuilib/AltosUISeries.java
+++ b/altosuilib/AltosUISeries.java
@@ -15,14 +15,14 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
-import org.altusmetrum.altoslib_3.*;
+import org.altusmetrum.altoslib_4.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
@@ -38,7 +38,7 @@ class AltosUITime extends AltosUnits {
public double value(double v, boolean imperial_units) { return v; }
public double inverse(double v, boolean imperial_unis) { return v; }
-
+
public String show_units(boolean imperial_units) { return "s"; }
public String say_units(boolean imperial_units) { return "seconds"; }
@@ -60,7 +60,7 @@ public class AltosUISeries extends XYSeries implements AltosUIGrapher {
XYItemRenderer renderer;
int fetch;
boolean enable;
-
+
public void set_units() {
axis.set_units();
StandardXYToolTipGenerator ttg;
@@ -104,6 +104,7 @@ public class AltosUISeries extends XYSeries implements AltosUIGrapher {
renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, color);
+ renderer.setSeriesStroke(0, new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
renderer.setSeriesVisible(0, enable);
set_units();
}
diff --git a/altosuilib/AltosUIUnitsIndicator.java b/altosuilib/AltosUIUnitsIndicator.java
new file mode 100644
index 00000000..2285b6fc
--- /dev/null
+++ b/altosuilib/AltosUIUnitsIndicator.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+
+public abstract class AltosUIUnitsIndicator extends AltosUIIndicator {
+
+ AltosUnits units;
+
+ abstract public double value(AltosState state, int i);
+ public double good() { return 0; }
+ public boolean good(double value) { return value != AltosLib.MISSING && value >= good(); }
+ public boolean hide(double value) { return false; }
+
+ public boolean hide(AltosState state, int i) {
+ if (state == null)
+ return hide(AltosLib.MISSING);
+ return hide(value(state, i));
+ }
+
+ public double value (AltosState state, AltosListenerState listener_state, int i) {
+ return value(state, i);
+ }
+
+ public double[] last_values;
+
+ public void show(double... v) {
+ show();
+ for (int i = 0; i < values.length; i++) {
+ if (v[i] != last_values[i]) {
+ String value_text;
+ boolean good = false;
+
+ if (v[i] == AltosLib.MISSING) {
+ value_text = "Missing";
+ } else {
+ value_text = units.show(8, v[i]);
+ if (i == 0)
+ good = good(v[i]);
+ }
+ last_values[i] = v[i];
+ if (i == 0 && lights != null)
+ set_lights(good);
+ values[i].setText(value_text);
+ }
+ }
+ }
+
+ public void units_changed(boolean imperial_units) {
+ show(last_values);
+ }
+
+ public void show (AltosState state, AltosListenerState listener_state) {
+ double[] v = new double[values.length];
+ boolean hide = false;
+
+ for (int i = 0; i < values.length; i++) {
+ if (state != null)
+ v[i] = value(state, listener_state, i);
+ else
+ v[i] = AltosLib.MISSING;
+ if (hide(state, i))
+ hide = true;
+ }
+
+ if (hide)
+ hide();
+ else
+ show(v);
+ }
+
+ public void reset() {
+ for (int i = 0; i < last_values.length; i++)
+ last_values[i] = AltosLib.MISSING - 1;
+ }
+
+ public AltosUIUnitsIndicator (Container container, int x, int y, int label_width, AltosUnits units, String name, int number_values, boolean has_lights, int width) {
+ super(container, x, y, label_width, name, number_values, has_lights, width);
+ this.units = units;
+ last_values = new double[values.length];
+ for (int i = 0; i < last_values.length; i++)
+ last_values[i] = AltosLib.MISSING - 1;
+ }
+
+ public AltosUIUnitsIndicator (Container container, int x, int y, AltosUnits units, String name, int number_values, boolean has_lights, int width) {
+ this(container, x, y, 1, units, name, number_values, has_lights, width);
+ }
+
+ public AltosUIUnitsIndicator (Container container, int y, AltosUnits units, String name, int number_values, boolean has_lights, int width) {
+ this(container, 0, y, units, name, number_values, has_lights, width);
+ }
+
+ public AltosUIUnitsIndicator (Container container, int y, AltosUnits units, String name, int width) {
+ this(container, 0, y, units, name, 1, false, width);
+ }
+
+ public AltosUIUnitsIndicator (Container container, int y, AltosUnits units, String name) {
+ this(container, 0, y, units, name, 1, false, 1);
+ }
+
+ public AltosUIUnitsIndicator (Container container, int x,int y, AltosUnits units, String name) {
+ this(container, x, y, units, name, 1, false, 1);
+ }
+}
diff --git a/altosuilib/AltosUIVersion.java.in b/altosuilib/AltosUIVersion.java.in
index 169cca1b..0edb5c04 100644
--- a/altosuilib/AltosUIVersion.java.in
+++ b/altosuilib/AltosUIVersion.java.in
@@ -15,8 +15,14 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
public class AltosUIVersion {
public final static String version = "@VERSION@";
+
+ public final static String google_maps_api_key = @GOOGLEKEY@;
+
+ static boolean has_google_maps_api_key() {
+ return google_maps_api_key != null && google_maps_api_key.length() > 1;
+ }
}
diff --git a/altosuilib/AltosUIVoltageIndicator.java b/altosuilib/AltosUIVoltageIndicator.java
new file mode 100644
index 00000000..3ff17213
--- /dev/null
+++ b/altosuilib/AltosUIVoltageIndicator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+
+public abstract class AltosUIVoltageIndicator extends AltosUIUnitsIndicator {
+
+ abstract public double voltage(AltosState state);
+ abstract public double good();
+
+ public double value(AltosState state, int i) {
+ return voltage(state);
+ }
+
+ double last_voltage = -1;
+
+ public AltosUIVoltageIndicator (Container container, int x, int y, String name, int width) {
+ super(container, x, y, AltosConvert.voltage, name, 1, true, width);
+ }
+
+ public AltosUIVoltageIndicator (Container container, int y, String name, int width) {
+ this(container, 0, y, name, width);
+ }
+}
diff --git a/altosuilib/AltosUSBDevice.java b/altosuilib/AltosUSBDevice.java
index 4f329840..b70b5e83 100644
--- a/altosuilib/AltosUSBDevice.java
+++ b/altosuilib/AltosUSBDevice.java
@@ -15,7 +15,7 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_1;
+package org.altusmetrum.altosuilib_2;
import java.util.*;
import libaltosJNI.*;
diff --git a/altosuilib/AltosVoice.java b/altosuilib/AltosVoice.java
new file mode 100644
index 00000000..a3995f68
--- /dev/null
+++ b/altosuilib/AltosVoice.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import com.sun.speech.freetts.Voice;
+import com.sun.speech.freetts.VoiceManager;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class AltosVoice implements Runnable {
+ VoiceManager voice_manager;
+ Voice voice;
+ LinkedBlockingQueue<String> phrases;
+ Thread thread;
+ boolean busy;
+
+ final static String voice_name = "kevin16";
+
+ public void run() {
+ try {
+ for (;;) {
+ String s = phrases.take();
+ voice.speak(s);
+ synchronized(this) {
+ if (phrases.isEmpty()) {
+ busy = false;
+ notifyAll();
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+
+ public synchronized void drain() throws InterruptedException {
+ while (busy)
+ wait();
+ }
+
+ public void speak_always(String s) {
+ try {
+ if (voice != null) {
+ synchronized(this) {
+ busy = true;
+ phrases.put(s);
+ }
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+
+ public void speak(String s) {
+ if (AltosUIPreferences.voice())
+ speak_always(s);
+ }
+
+ public void speak(String format, Object... parameters) {
+ speak(String.format(format, parameters));
+ }
+
+ public AltosVoice () {
+ busy = false;
+ voice_manager = VoiceManager.getInstance();
+ voice = voice_manager.getVoice(voice_name);
+ if (voice != null) {
+ voice.allocate();
+ phrases = new LinkedBlockingQueue<String> ();
+ thread = new Thread(this);
+ thread.start();
+ } else {
+ System.out.printf("Voice manager failed to open %s\n", voice_name);
+ Voice[] voices = voice_manager.getVoices();
+ System.out.printf("Available voices:\n");
+ for (int i = 0; i < voices.length; i++) {
+ System.out.println(" " + voices[i].getName()
+ + " (" + voices[i].getDomain() + " domain)");
+ }
+ }
+ }
+}
diff --git a/altosuilib/GrabNDrag.java b/altosuilib/GrabNDrag.java
new file mode 100644
index 00000000..4426f7a3
--- /dev/null
+++ b/altosuilib/GrabNDrag.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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 org.altusmetrum.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.MouseInputAdapter;
+
+class GrabNDrag extends MouseInputAdapter {
+ private JComponent scroll;
+ private Point startPt = new Point();
+
+ public GrabNDrag(JComponent scroll) {
+ this.scroll = scroll;
+ scroll.addMouseMotionListener(this);
+ scroll.addMouseListener(this);
+ scroll.setAutoscrolls(true);
+ }
+
+ public static boolean grab_n_drag(MouseEvent e) {
+ return e.getModifiers() == InputEvent.BUTTON1_MASK;
+ }
+
+ public void mousePressed(MouseEvent e) {
+ if (grab_n_drag(e))
+ startPt.setLocation(e.getPoint());
+ }
+ public void mouseDragged(MouseEvent e) {
+ if (grab_n_drag(e)) {
+ int xd = e.getX() - startPt.x;
+ int yd = e.getY() - startPt.y;
+
+ Rectangle r = scroll.getVisibleRect();
+ r.x -= xd;
+ r.y -= yd;
+ scroll.scrollRectToVisible(r);
+ }
+ }
+}
diff --git a/altosuilib/Makefile.am b/altosuilib/Makefile.am
index 4b22af1f..e08fbe74 100644
--- a/altosuilib/Makefile.am
+++ b/altosuilib/Makefile.am
@@ -1,4 +1,4 @@
-AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -source 6
+AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6
JAVAROOT=bin
@@ -9,8 +9,10 @@ SRC=.
altosuilibdir = $(datadir)/java
altosuilib_JAVA = \
+ GrabNDrag.java \
AltosDevice.java \
AltosDeviceDialog.java \
+ AltosFlightDisplay.java \
AltosFontListener.java \
AltosPositionListener.java \
AltosUIConfigure.java \
@@ -30,10 +32,70 @@ altosuilib_JAVA = \
AltosUIPreferences.java \
AltosUISeries.java \
AltosUIVersion.java \
- AltosUSBDevice.java
+ AltosUSBDevice.java \
+ AltosVoice.java \
+ AltosDisplayThread.java \
+ AltosDeviceUIDialog.java \
+ AltosFreqList.java \
+ AltosSerial.java \
+ AltosSerialInUseException.java \
+ AltosConfigFreqUI.java \
+ AltosScanUI.java \
+ AltosEepromDelete.java \
+ AltosEepromManage.java \
+ AltosEepromMonitorUI.java \
+ AltosEepromSelect.java \
+ AltosCSVUI.java \
+ AltosDataChooser.java \
+ AltosLights.java \
+ AltosLed.java \
+ AltosFlashUI.java \
+ AltosRomconfigUI.java \
+ AltosInfoTable.java \
+ AltosFlightInfoTableModel.java \
+ AltosFlightStatsTable.java \
+ AltosGraph.java \
+ AltosGraphDataPoint.java \
+ AltosGraphDataSet.java \
+ AltosBTDevice.java \
+ AltosBTDeviceIterator.java \
+ AltosBTManage.java \
+ AltosBTKnown.java \
+ AltosUIMap.java \
+ AltosUIMapView.java \
+ AltosUIMapLine.java \
+ AltosUIMapMark.java \
+ AltosUIMapPath.java \
+ AltosUIMapTile.java \
+ AltosUIMapCache.java \
+ AltosUIMapImage.java \
+ AltosUIMapTransform.java \
+ AltosUIMapRectangle.java \
+ AltosUIMapZoomListener.java \
+ AltosUIMapTileListener.java \
+ AltosUIMapPreload.java \
+ AltosUIMapStore.java \
+ AltosUIMapStoreListener.java \
+ AltosUILatLon.java \
+ AltosUIFlightTab.java \
+ AltosUIIndicator.java \
+ AltosUIUnitsIndicator.java \
+ AltosUIVoltageIndicator.java
JAR=altosuilib_$(ALTOSUILIB_VERSION).jar
+# Icons
+ICONDIR=$(top_srcdir)/icon
+
+ICONS= $(ICONDIR)/redled.png $(ICONDIR)/redoff.png \
+ $(ICONDIR)/greenled.png $(ICONDIR)/greenoff.png \
+ $(ICONDIR)/grayon.png $(ICONDIR)/grayled.png
+
+# icon base names for jar
+ICONJAR= -C $(ICONDIR) redled.png -C $(ICONDIR) redoff.png \
+ -C $(ICONDIR) greenled.png -C $(ICONDIR) greenoff.png \
+ -C $(ICONDIR) grayon.png -C $(ICONDIR) grayled.png
+
all-local: $(JAR)
clean-local:
@@ -48,5 +110,5 @@ install-altosuilibJAVA: $(JAR)
$(JAVAROOT):
mkdir -p $(JAVAROOT)
-$(JAR): classaltosuilib.stamp
- jar cf $@ -C $(JAVAROOT) .
+$(JAR): classaltosuilib.stamp $(ICONS)
+ jar cf $@ $(ICONJAR) -C $(JAVAROOT) .