Initial Commit - Copy from Altus Metrum AltOS
This commit is contained in:
3
altosuilib/.gitignore
vendored
Normal file
3
altosuilib/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
bin
|
||||
classaltosuilib.stamp
|
||||
altosuilib*.jar
|
||||
129
altosuilib/AltosBTDevice.java
Normal file
129
altosuilib/AltosBTDevice.java
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import libaltosJNI.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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 int hashCode() {
|
||||
return getName().hashCode() ^ getAddr().hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null)
|
||||
return false;
|
||||
|
||||
if (!(o instanceof AltosBTDevice))
|
||||
return false;
|
||||
AltosBTDevice other = (AltosBTDevice) o;
|
||||
return getName().equals(other.getName()) && getAddr().equals(other.getAddr());
|
||||
}
|
||||
|
||||
public AltosBTDevice(String name, String addr) {
|
||||
AltosUILib.load_library();
|
||||
libaltos.altos_bt_fill_in(name, addr,this);
|
||||
}
|
||||
|
||||
public AltosBTDevice() {
|
||||
}
|
||||
}
|
||||
65
altosuilib/AltosBTDeviceIterator.java
Normal file
65
altosuilib/AltosBTDeviceIterator.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.util.*;
|
||||
import libaltosJNI.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
102
altosuilib/AltosBTKnown.java
Normal file
102
altosuilib/AltosBTKnown.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.util.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
354
altosuilib/AltosBTManage.java
Normal file
354
altosuilib/AltosBTManage.java
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
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_14.*;
|
||||
|
||||
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 = getScrollablePane();
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
106
altosuilib/AltosCSVUI.java
Normal file
106
altosuilib/AltosCSVUI.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import java.io.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosCSVUI
|
||||
extends AltosUIDialog
|
||||
implements ActionListener
|
||||
{
|
||||
JFileChooser csv_chooser;
|
||||
JPanel accessory;
|
||||
JComboBox<String> combo_box;
|
||||
AltosFlightSeries series;
|
||||
AltosCalData cal_data;
|
||||
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, AltosFlightSeries series, File source_file) {
|
||||
this.series = series;
|
||||
this.cal_data = series.cal_data();
|
||||
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(series);
|
||||
writer.close();
|
||||
} catch (FileNotFoundException ee) {
|
||||
JOptionPane.showMessageDialog(frame,
|
||||
ee.getMessage(),
|
||||
"Cannot open file",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
413
altosuilib/AltosConfigFreqUI.java
Normal file
413
altosuilib/AltosConfigFreqUI.java
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.text.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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 = AltosParse.parse_double_locale(f_s);
|
||||
|
||||
return new AltosFrequency(f_d, d_s);
|
||||
} catch (ParseException ne) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public AltosEditFreqUI(Frame in_frame, AltosFrequency existing) {
|
||||
super(in_frame, true);
|
||||
|
||||
got_ok = false;
|
||||
frame = in_frame;
|
||||
|
||||
Container pane = getScrollablePane();
|
||||
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 = getScrollablePane();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
70
altosuilib/AltosDataChooser.java
Normal file
70
altosuilib/AltosDataChooser.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import java.io.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosDataChooser extends JFileChooser {
|
||||
JFrame frame;
|
||||
String filename;
|
||||
File file;
|
||||
|
||||
public String filename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public File file() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public AltosRecordSet runDialog() {
|
||||
int ret;
|
||||
|
||||
ret = showOpenDialog(frame);
|
||||
if (ret == APPROVE_OPTION) {
|
||||
file = getSelectedFile();
|
||||
if (file == null)
|
||||
return null;
|
||||
try {
|
||||
return AltosLib.record_set(file);
|
||||
} catch (IOException ie) {
|
||||
JOptionPane.showMessageDialog(frame,
|
||||
ie.getMessage(),
|
||||
"Error reading 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());
|
||||
}
|
||||
}
|
||||
32
altosuilib/AltosDevice.java
Normal file
32
altosuilib/AltosDevice.java
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import libaltosJNI.*;
|
||||
|
||||
public interface AltosDevice {
|
||||
public abstract String toString();
|
||||
public abstract String toShortString();
|
||||
public abstract int getSerial();
|
||||
public abstract String getPath();
|
||||
public abstract boolean matchProduct(int product);
|
||||
public abstract String getErrorString();
|
||||
public SWIGTYPE_p_altos_file open();
|
||||
public abstract boolean equals(Object obj);
|
||||
}
|
||||
190
altosuilib/AltosDeviceDialog.java
Normal file
190
altosuilib/AltosDeviceDialog.java
Normal file
@@ -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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
public abstract class AltosDeviceDialog extends AltosUIDialog implements ActionListener {
|
||||
|
||||
private AltosDevice value;
|
||||
private JList<AltosDevice> list;
|
||||
private JButton cancel_button;
|
||||
private JButton select_button;
|
||||
public Frame frame;
|
||||
public int product;
|
||||
public JPanel buttonPane;
|
||||
private Timer timer;
|
||||
AltosDevice[] devices;
|
||||
|
||||
public AltosDevice getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public abstract AltosDevice[] devices();
|
||||
|
||||
public void update_devices() {
|
||||
AltosDevice selected = list.getSelectedValue();
|
||||
|
||||
devices = devices();
|
||||
list.setListData(devices);
|
||||
for (AltosDevice d : devices) {
|
||||
if (d.equals(selected)) {
|
||||
list.setSelectedValue(d, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
select_button.setEnabled(devices.length > 0);
|
||||
}
|
||||
|
||||
public void add_bluetooth() { }
|
||||
|
||||
public AltosDeviceDialog (Frame in_frame, Component location, int in_product) {
|
||||
super(in_frame, "Device Selection", true);
|
||||
|
||||
product = in_product;
|
||||
frame = in_frame;
|
||||
value = null;
|
||||
|
||||
cancel_button = new JButton("Cancel");
|
||||
cancel_button.setActionCommand("cancel");
|
||||
cancel_button.addActionListener(this);
|
||||
|
||||
select_button = new JButton("Select");
|
||||
select_button.setActionCommand("select");
|
||||
select_button.addActionListener(this);
|
||||
select_button.setEnabled(false);
|
||||
getRootPane().setDefaultButton(select_button);
|
||||
|
||||
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
|
||||
//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) {
|
||||
select_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));
|
||||
|
||||
//Lay out the buttons from left to right.
|
||||
buttonPane = new JPanel();
|
||||
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
|
||||
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
|
||||
buttonPane.add(Box.createHorizontalGlue());
|
||||
buttonPane.add(cancel_button);
|
||||
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
|
||||
|
||||
if (AltosUILib.has_bluetooth)
|
||||
add_bluetooth();
|
||||
|
||||
buttonPane.add(select_button);
|
||||
|
||||
//Put everything together, using the content pane's BorderLayout.
|
||||
Container contentPane = getContentPane();
|
||||
contentPane.add(listPane, BorderLayout.CENTER);
|
||||
contentPane.add(buttonPane, BorderLayout.PAGE_END);
|
||||
|
||||
//Initialize values.
|
||||
update_devices();
|
||||
if (devices != null && devices.length != 0)
|
||||
list.setSelectedValue(devices[0], true);
|
||||
pack();
|
||||
setLocationRelativeTo(location);
|
||||
|
||||
timer = new Timer(1000, new ActionListener () {
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
update_devices();
|
||||
}
|
||||
});
|
||||
|
||||
addComponentListener(new ComponentListener() {
|
||||
public void componentShown(ComponentEvent e) {
|
||||
timer.start();
|
||||
}
|
||||
public void componentMoved(ComponentEvent e) {
|
||||
}
|
||||
public void componentResized(ComponentEvent e) {
|
||||
}
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
timer.stop();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//Handle clicks on the Set and Cancel buttons.
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if ("select".equals(e.getActionCommand())) {
|
||||
value = (AltosDevice)(list.getSelectedValue());
|
||||
setVisible(false);
|
||||
}
|
||||
if ("cancel".equals(e.getActionCommand()))
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
95
altosuilib/AltosDeviceUIDialog.java
Normal file
95
altosuilib/AltosDeviceUIDialog.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
|
||||
public class AltosDeviceUIDialog extends AltosDeviceDialog {
|
||||
|
||||
boolean include_bluetooth;
|
||||
|
||||
public AltosDevice[] devices() {
|
||||
java.util.List<AltosDevice> usb_devices = AltosUSBDevice.list(product);
|
||||
int num_devices = usb_devices.size();
|
||||
|
||||
java.util.List<AltosDevice> bt_devices = null;
|
||||
|
||||
if (include_bluetooth) {
|
||||
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);
|
||||
if (include_bluetooth) {
|
||||
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() {
|
||||
if (include_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, boolean include_bluetooth) {
|
||||
super(in_frame, location, in_product);
|
||||
this.include_bluetooth = include_bluetooth;
|
||||
}
|
||||
|
||||
public AltosDeviceUIDialog (Frame in_frame, Component location, int in_product) {
|
||||
this(in_frame, location, in_product, true);
|
||||
}
|
||||
|
||||
public static AltosDevice show (Component frameComp, int product, boolean include_bluetooth) {
|
||||
Frame frame = JOptionPane.getFrameForComponent(frameComp);
|
||||
AltosDeviceUIDialog dialog;
|
||||
|
||||
dialog = new AltosDeviceUIDialog(frame, frameComp, product, include_bluetooth);
|
||||
dialog.setVisible(true);
|
||||
return dialog.getValue();
|
||||
}
|
||||
|
||||
public static AltosDevice show (Component frameComp, int product) {
|
||||
return show(frameComp, product, true);
|
||||
}
|
||||
|
||||
public static AltosUSBDevice show_usb (Component frameComp, int product) {
|
||||
return (AltosUSBDevice) show(frameComp, product, false);
|
||||
}
|
||||
}
|
||||
261
altosuilib/AltosDisplayThread.java
Normal file
261
altosuilib/AltosDisplayThread.java
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosDisplayThread extends Thread {
|
||||
|
||||
Frame parent;
|
||||
IdleThread idle_thread;
|
||||
AltosVoice voice;
|
||||
AltosFlightReader reader;
|
||||
AltosState state;
|
||||
int old_state = AltosLib.ao_flight_invalid;
|
||||
boolean old_gps_ready = false;
|
||||
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, distance %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.distance));
|
||||
} 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, distance %s.",
|
||||
(int) (state.from_pad.bearing + 0.5),
|
||||
AltosConvert.distance.say_units(state.from_pad.distance));
|
||||
++reported_landing;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void notice(boolean spoken) {
|
||||
if (old_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 != state.state()) {
|
||||
if (state.state() != AltosLib.ao_flight_stateless)
|
||||
voice.speak(state.state_name());
|
||||
if ((old_state == AltosLib.ao_flight_invalid || old_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 == AltosLib.ao_flight_invalid || old_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_gps_ready != state.gps_ready) {
|
||||
if (state.gps_ready) {
|
||||
voice.speak("GPS ready");
|
||||
ret = true;
|
||||
}
|
||||
else if (old_gps_ready) {
|
||||
voice.speak("GPS lost");
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
old_state = state.state();
|
||||
old_gps_ready = state.gps_ready;
|
||||
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) {
|
||||
listener_state.running = false;
|
||||
break;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
146
altosuilib/AltosEepromDelete.java
Normal file
146
altosuilib/AltosEepromDelete.java
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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(200000);
|
||||
if (line == null)
|
||||
throw new TimeoutException();
|
||||
if (line.equals("Erased"))
|
||||
break;
|
||||
if (line.equals("Failed to erase"))
|
||||
throw new IOException(line);
|
||||
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.delete_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;
|
||||
}
|
||||
}
|
||||
22
altosuilib/AltosEepromGrapher.java
Normal file
22
altosuilib/AltosEepromGrapher.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2017 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, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public interface AltosEepromGrapher {
|
||||
|
||||
public void graph_flights(AltosEepromList list);
|
||||
}
|
||||
258
altosuilib/AltosEepromManage.java
Normal file
258
altosuilib/AltosEepromManage.java
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosEepromManage implements ActionListener {
|
||||
|
||||
JFrame frame;
|
||||
boolean remote;
|
||||
AltosDevice device;
|
||||
AltosSerial serial_line;
|
||||
AltosEepromList flights;
|
||||
AltosEepromDownload download;
|
||||
AltosEepromDelete delete;
|
||||
AltosEepromGrapher grapher;
|
||||
|
||||
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.delete_selected)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private String showDeletedFlights() {
|
||||
String result = "";
|
||||
|
||||
for (AltosEepromLog flight : flights) {
|
||||
if (flight.delete_selected) {
|
||||
if (result.equals(""))
|
||||
result = String.format("%d", flight.flight);
|
||||
else
|
||||
result = String.format("%s, %d", result, flight.flight);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean delete_start() {
|
||||
|
||||
boolean any_selected = false;
|
||||
for (AltosEepromLog flight : flights)
|
||||
any_selected = any_selected || flight.delete_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 graph_start() {
|
||||
boolean any_selected = false;
|
||||
for (AltosEepromLog flight : flights) {
|
||||
if (!flight.download_selected)
|
||||
flight.graph_selected = false;
|
||||
any_selected = any_selected || flight.graph_selected;
|
||||
}
|
||||
if (any_selected && grapher != null)
|
||||
grapher.graph_flights(flights);
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String cmd = e.getActionCommand();
|
||||
boolean success = e.getID() != 0;
|
||||
boolean running = false;
|
||||
|
||||
if (cmd.equals("download")) {
|
||||
if (success) {
|
||||
running = delete_start();
|
||||
if (!running)
|
||||
graph_start();
|
||||
}
|
||||
} 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);
|
||||
graph_start();
|
||||
}
|
||||
}
|
||||
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, grapher != null);
|
||||
|
||||
if (select.run()) {
|
||||
boolean any_selected = false;
|
||||
for (AltosEepromLog flight : flights)
|
||||
any_selected = any_selected || flight.download_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 = delete_start();
|
||||
if (!running)
|
||||
graph_start();
|
||||
}
|
||||
}
|
||||
}
|
||||
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, AltosEepromGrapher grapher, int product) {
|
||||
|
||||
//boolean running = false;
|
||||
|
||||
frame = given_frame;
|
||||
this.grapher = grapher;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
305
altosuilib/AltosEepromMonitorUI.java
Normal file
305
altosuilib/AltosEepromMonitorUI.java
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
class result_holder {
|
||||
static int result;
|
||||
}
|
||||
|
||||
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;
|
||||
JButton cancel;
|
||||
JProgressBar pbar;
|
||||
ActionListener listener;
|
||||
|
||||
static final int progress_max = 10000;
|
||||
|
||||
public AltosEepromMonitorUI(JFrame owner) {
|
||||
super (owner, "Download Flight Data", false);
|
||||
|
||||
setMinimumSize(new Dimension(600, 100));
|
||||
|
||||
this.owner = owner;
|
||||
|
||||
GridBagConstraints c;
|
||||
Insets il = new Insets(4,4,4,4);
|
||||
Insets ir = new Insets(4,4,4,4);
|
||||
|
||||
pane = getScrollablePane();
|
||||
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);
|
||||
|
||||
pbar = new JProgressBar();
|
||||
pbar.setMinimum(0);
|
||||
pbar.setMaximum(progress_max);
|
||||
pbar.setStringPainted(true);
|
||||
set_block_internal(0);
|
||||
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_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);
|
||||
}
|
||||
|
||||
int max_block = 1;
|
||||
|
||||
public void set_block_internal(int block) {
|
||||
double pos;
|
||||
String s;
|
||||
|
||||
pos = (double) block / (double) max_block;
|
||||
|
||||
s = String.format("block %d of %d", block, max_block);
|
||||
|
||||
pbar.setString(s);
|
||||
pbar.setStringPainted(true);
|
||||
pbar.setValue((int) (pos * progress_max));
|
||||
}
|
||||
|
||||
public void set_max(int max_block) {
|
||||
this.max_block = max_block;
|
||||
}
|
||||
|
||||
public void set_block(int in_block) {
|
||||
final int block = in_block;
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
set_block_internal(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 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_max(1);
|
||||
set_block_internal(0);
|
||||
set_flight_internal(0);
|
||||
}
|
||||
|
||||
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 Boolean check_overwrite(File in_file) {
|
||||
final Semaphore check_overwrite_done = new Semaphore(0);
|
||||
final File file = in_file;
|
||||
final result_holder result = new result_holder();
|
||||
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
result_holder.result = JOptionPane.showConfirmDialog(owner,
|
||||
String.format("\"%s\" already exists, overwrite?",
|
||||
file.toString()),
|
||||
"Overwrite Existing File?",
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
check_overwrite_done.release();
|
||||
}
|
||||
};
|
||||
|
||||
SwingUtilities.invokeLater(r);
|
||||
try {
|
||||
check_overwrite_done.acquire();
|
||||
} catch (Exception e) {}
|
||||
return result_holder.result == JOptionPane.YES_OPTION;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
234
altosuilib/AltosEepromSelect.java
Normal file
234
altosuilib/AltosEepromSelect.java
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
class AltosEepromItem implements ActionListener {
|
||||
AltosEepromLog log;
|
||||
JLabel label;
|
||||
JCheckBox download;
|
||||
JCheckBox delete;
|
||||
JCheckBox graph;
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
log.download_selected = download.isSelected();
|
||||
log.delete_selected = delete.isSelected();
|
||||
log.graph_selected = graph.isSelected();
|
||||
}
|
||||
|
||||
public AltosEepromItem(AltosEepromLog in_log) {
|
||||
log = in_log;
|
||||
|
||||
String text;
|
||||
if (log.flight >= 0)
|
||||
text = String.format("Flight #%02d", log.flight);
|
||||
else
|
||||
text = String.format("Corrupt #%02d", -log.flight);
|
||||
|
||||
label = new JLabel(text);
|
||||
|
||||
download = new JCheckBox("", log.download_selected);
|
||||
download.addActionListener(this);
|
||||
|
||||
delete = new JCheckBox("", log.delete_selected);
|
||||
delete.addActionListener(this);
|
||||
|
||||
graph = new JCheckBox("", log.graph_selected);
|
||||
graph.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,
|
||||
boolean has_graph) {
|
||||
|
||||
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"), 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("Download");
|
||||
flightPane.add(downloadHeaderLabel, c);
|
||||
|
||||
/* Delete Header */
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 2; c.gridy = 0;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.weightx = 0.5;
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
c.insets = i;
|
||||
JLabel deleteHeaderLabel = new JLabel("Delete");
|
||||
flightPane.add(deleteHeaderLabel, c);
|
||||
|
||||
if (has_graph) {
|
||||
/* Graph Header */
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 3; c.gridy = 0;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.weightx = 0.5;
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
c.insets = i;
|
||||
JLabel graphHeaderLabel = new JLabel("Graph");
|
||||
flightPane.add(graphHeaderLabel, 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 download 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.download, c);
|
||||
|
||||
/* Add delete checkbox for the flight */
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 2; c.gridy = itemNumber;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.weightx = 0.5;
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
c.insets = i;
|
||||
flightPane.add(item.delete, c);
|
||||
|
||||
if (has_graph) {
|
||||
/* Add graph checkbox for the flight */
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 3; c.gridy = itemNumber;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.weightx = 0.5;
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
c.insets = i;
|
||||
flightPane.add(item.graph, 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();
|
||||
}
|
||||
}
|
||||
759
altosuilib/AltosFlashUI.java
Normal file
759
altosuilib/AltosFlashUI.java
Normal file
@@ -0,0 +1,759 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
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_14.*;
|
||||
|
||||
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
|
||||
AltosUSBDevice device;
|
||||
|
||||
AltosLink link;
|
||||
|
||||
// Desired Rom configuration
|
||||
AltosRomconfig rom_config;
|
||||
|
||||
// Flash controller
|
||||
AltosProgrammer programmer;
|
||||
|
||||
private static final String[] pair_programmed_files = {
|
||||
"teleballoon",
|
||||
"telebt-v1",
|
||||
"teledongle-v0",
|
||||
"telefire-v0",
|
||||
"telemetrum-v0",
|
||||
"telemetrum-v1",
|
||||
"telemini-v1",
|
||||
"telenano",
|
||||
"teleshield"
|
||||
};
|
||||
|
||||
private static final String[] pair_programmed_devices = {
|
||||
"TeleBalloon",
|
||||
"TeleBT-v1",
|
||||
"TeleDongle-v0",
|
||||
"TeleFire-v0",
|
||||
"TeleFire",
|
||||
"TeleMetrum-v0",
|
||||
"TeleMetrum-v1",
|
||||
"TeleMini-v1",
|
||||
"TeleNano",
|
||||
"TeleShield"
|
||||
};
|
||||
|
||||
private static final String[] log_erased_devices = {
|
||||
"TeleGPS"
|
||||
};
|
||||
|
||||
private boolean is_pair_programmed() {
|
||||
|
||||
if (file != null) {
|
||||
String name = file.getName();
|
||||
for (int i = 0; i < pair_programmed_files.length; i++) {
|
||||
if (name.startsWith(pair_programmed_files[i]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (device != null) {
|
||||
String name = device.toString();
|
||||
for (int i = 0; i < pair_programmed_devices.length; i++) {
|
||||
if (name.startsWith(pair_programmed_devices[i]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean is_log_erased() {
|
||||
if (device != null) {
|
||||
String name = device.toString();
|
||||
for (int i = 0; i < log_erased_devices.length; i++) {
|
||||
if (name.startsWith(log_erased_devices[i]))
|
||||
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(AltosFlashListener.flash_done)) {
|
||||
setVisible(false);
|
||||
dispose();
|
||||
} else if (cmd.equals(AltosFlashListener.flash_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 = getScrollablePane();
|
||||
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 {
|
||||
String head;
|
||||
String description;
|
||||
|
||||
public AltosHexfileFilter(String usb_product) {
|
||||
int l;
|
||||
int dash;
|
||||
|
||||
/* Trim off any trailing variants (1.0a vs 1.0) */
|
||||
for (dash = usb_product.length(); dash > 0; dash--) {
|
||||
char c = usb_product.charAt(dash-1);
|
||||
if (c == '-')
|
||||
break;
|
||||
}
|
||||
if (dash == 0)
|
||||
dash = usb_product.length();
|
||||
|
||||
for (l = usb_product.length(); l > dash; l--) {
|
||||
char c = usb_product.charAt(l-1);
|
||||
if (c < 'a' || 'z' < c)
|
||||
break;
|
||||
}
|
||||
head = usb_product.substring(0, l).toLowerCase();
|
||||
description = String.format("%s Image File", usb_product);
|
||||
}
|
||||
|
||||
public boolean accept(File file) {
|
||||
return !file.isFile() || (file.getName().startsWith(head) && file.getName().endsWith(".ihx"));
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
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)) {
|
||||
AltosHexfileFilter filter = new AltosHexfileFilter(device.usb_product());
|
||||
hexfile_chooser.addChoosableFileFilter(filter);
|
||||
hexfile_chooser.setFileFilter(filter);
|
||||
}
|
||||
|
||||
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_usb(frame, AltosLib.product_any);
|
||||
|
||||
if (device == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean rom_config_matches (AltosRomconfig a, AltosRomconfig b) {
|
||||
if (a == null || b == null)
|
||||
return (a == null && b == null);
|
||||
|
||||
if (!a.valid || !b.valid)
|
||||
return false;
|
||||
|
||||
if (a.usb_id != null && b.usb_id != null &&
|
||||
(a.usb_id.vid != b.usb_id.vid ||
|
||||
a.usb_id.pid != b.usb_id.pid))
|
||||
return false;
|
||||
|
||||
if (a.usb_product != null && b.usb_product != null &&
|
||||
!a.usb_product.equals(b.usb_product))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean update_rom_config_info(AltosRomconfig existing_config, AltosRomconfig image_config) {
|
||||
AltosRomconfig new_config;
|
||||
|
||||
if (!rom_config_matches(existing_config, image_config)) {
|
||||
int ret;
|
||||
if (existing_config == null || !existing_config.valid) {
|
||||
ret = JOptionPane.showConfirmDialog(this,
|
||||
String.format("Cannot determine target device type\nImage is %04x:%04x %s\nFlash anyways?",
|
||||
image_config.usb_id.vid,
|
||||
image_config.usb_id.pid,
|
||||
image_config.usb_product),
|
||||
"Unknown Target Device",
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
} else {
|
||||
ret = JOptionPane.showConfirmDialog(this,
|
||||
String.format("Device is %04x:%04x %s\nImage is %04x:%04x %s\nFlash anyways?",
|
||||
existing_config.usb_id.vid,
|
||||
existing_config.usb_id.pid,
|
||||
existing_config.usb_product,
|
||||
image_config.usb_id.vid,
|
||||
image_config.usb_id.pid,
|
||||
image_config.usb_product),
|
||||
"Image doesn't match Device",
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
}
|
||||
if (ret != JOptionPane.YES_OPTION)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (existing_config != null && existing_config.radio_calibration_broken) {
|
||||
int ret = JOptionPane.showConfirmDialog(this,
|
||||
String.format("Radio calibration value %d may be incorrect\nFlash anyways?",
|
||||
existing_config.radio_calibration),
|
||||
"Radio Calibration Invalid",
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
if (ret != JOptionPane.YES_OPTION)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
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.target_romconfig(device.usb_id(), device.usb_product());
|
||||
|
||||
final AltosRomconfig image_config = programmer.image_romconfig();
|
||||
|
||||
System.out.printf("product %s current %s image %s\n", device.usb_product(), current_config, image_config);
|
||||
|
||||
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, image_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;
|
||||
|
||||
boolean erase_answer;
|
||||
|
||||
class open_task implements Runnable {
|
||||
AltosDevice device;
|
||||
Thread t;
|
||||
open_dialog dialog;
|
||||
AltosLink link;
|
||||
|
||||
public void do_exception(final Exception e) {
|
||||
if (link != null) {
|
||||
try {
|
||||
link.close();
|
||||
} catch (Exception ex) {}
|
||||
}
|
||||
SwingUtilities.invokeLater(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
try { dialog.open_exception(e); } catch (Exception ex) { }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void do_success(final AltosLink link) {
|
||||
SwingUtilities.invokeLater(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
try { dialog.open_success(link); } catch (Exception ex) { }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean do_notify_erase(final AltosConfigData config_data) {
|
||||
erase_answer = false;
|
||||
final Semaphore erase_answer_done = new Semaphore(0);
|
||||
SwingUtilities.invokeLater(
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
int ret = JOptionPane.showConfirmDialog(dialog.owner,
|
||||
String.format("Updating %s from firmware %s will erase stored data, continue?",
|
||||
config_data.product,
|
||||
config_data.version),
|
||||
"Erase Stored Data?",
|
||||
JOptionPane.YES_NO_OPTION);
|
||||
erase_answer = ret == JOptionPane.YES_OPTION;
|
||||
erase_answer_done.release();
|
||||
}
|
||||
});
|
||||
try {
|
||||
erase_answer_done.acquire();
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
return erase_answer;
|
||||
}
|
||||
|
||||
public void run () {
|
||||
link = null;
|
||||
try {
|
||||
boolean new_device = false;
|
||||
|
||||
for (;;) {
|
||||
System.out.printf("Attempting to open %s\n", device.toShortString());
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
link = new AltosSerial(device);
|
||||
|
||||
if (link != null)
|
||||
break;
|
||||
|
||||
if (!new_device)
|
||||
break;
|
||||
|
||||
System.out.printf("Waiting for device to become ready\n");
|
||||
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
if (link == null)
|
||||
throw new IOException(String.format("%s: open failed",
|
||||
device.toShortString()));
|
||||
|
||||
System.out.printf("Checking device ready\n");
|
||||
|
||||
/* See if the link is usable already */
|
||||
if (is_pair_programmed() || link.is_loader()) {
|
||||
System.out.printf("Device ready for use\n");
|
||||
do_success(link);
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.printf("Checking log erased\n");
|
||||
|
||||
if (is_log_erased()) {
|
||||
System.out.printf("Fetching config data\n");
|
||||
AltosConfigData config_data = link.config_data();
|
||||
System.out.printf("version %s\n", config_data.version);
|
||||
/* Completely erase TeleGPS flash when firmware is old */
|
||||
if (config_data.compare_version("1.9.7") < 0)
|
||||
{
|
||||
if (!do_notify_erase(config_data))
|
||||
throw new IOException(String.format("%s: not erasing log",
|
||||
device.toShortString()));
|
||||
System.out.printf("Erasing log\n");
|
||||
link.printf("Z DoIt\n");
|
||||
link.synchronize(120 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
java.util.List<AltosDevice> prev_devices =
|
||||
AltosUSBDevice.list(AltosLib.product_altusmetrum);
|
||||
|
||||
/* Nope, switch to loader and
|
||||
* wait for it to re-appear
|
||||
*/
|
||||
|
||||
System.out.printf("Switch to loader\n");
|
||||
|
||||
link.to_loader();
|
||||
|
||||
/* This is a bit fragile, but
|
||||
* I'm not sure what else to
|
||||
* do other than ask the user.
|
||||
*
|
||||
* Search for a device which
|
||||
* wasn't there before we
|
||||
* asked the target to switch
|
||||
* to loader mode
|
||||
*/
|
||||
|
||||
device = null;
|
||||
for (;;) {
|
||||
Thread.sleep(100);
|
||||
java.util.List<AltosDevice> devices =
|
||||
AltosUSBDevice.list(AltosLib.product_altusmetrum);
|
||||
|
||||
for (AltosDevice d : devices) {
|
||||
boolean matched = false;
|
||||
System.out.printf("\tfound device %s\n", d.toShortString());
|
||||
for (AltosDevice p : prev_devices)
|
||||
if (d.equals(p)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if (!matched) {
|
||||
System.out.printf("Identified new device %s\n", d.toShortString());
|
||||
device = (AltosUSBDevice) d;
|
||||
new_device = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (device != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (AltosSerialInUseException ee) {
|
||||
do_exception(ee);
|
||||
} catch (FileNotFoundException fe) {
|
||||
do_exception(fe);
|
||||
} catch (IOException ie) {
|
||||
do_exception (ie);
|
||||
} catch (TimeoutException te) {
|
||||
do_exception (te);
|
||||
} catch (InterruptedException ie) {
|
||||
do_exception (ie);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
t.interrupt();
|
||||
}
|
||||
|
||||
public open_task(AltosDevice device, open_dialog dialog) {
|
||||
this.device = device;
|
||||
this.dialog = dialog;
|
||||
t = new Thread(this);
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
class open_dialog
|
||||
extends AltosUIDialog
|
||||
implements ActionListener
|
||||
{
|
||||
AltosUIFrame owner;
|
||||
|
||||
private JLabel opening_label;
|
||||
private JButton cancel_button;
|
||||
|
||||
boolean done = false;
|
||||
|
||||
AltosLink link = null;
|
||||
|
||||
open_task open = null;
|
||||
|
||||
public void open_exception(Exception e) {
|
||||
System.out.printf("open_exception\n");
|
||||
setVisible(false);
|
||||
exception(e);
|
||||
done = true;
|
||||
}
|
||||
|
||||
public void open_success(AltosLink link) {
|
||||
System.out.printf("open_success\n");
|
||||
setVisible(false);
|
||||
this.link = link;
|
||||
done = true;
|
||||
}
|
||||
|
||||
public AltosLink do_open(open_task open) throws InterruptedException {
|
||||
this.open = open;
|
||||
setVisible(true);
|
||||
return link;
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String cmd = e.getActionCommand();
|
||||
|
||||
if (cmd.equals("cancel"))
|
||||
if (open != null)
|
||||
open.cancel();
|
||||
done = true;
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
public open_dialog(AltosUIFrame in_owner) {
|
||||
super(in_owner, "Open Flash Target Device", true);
|
||||
owner = in_owner;
|
||||
|
||||
Container pane = getScrollablePane();
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
Insets i = new Insets(4,4,4,4);
|
||||
|
||||
|
||||
pane.setLayout(new GridBagLayout());
|
||||
|
||||
opening_label = new JLabel("Opening Device");
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = i;
|
||||
c.weightx = 0;
|
||||
c.weighty = 0;
|
||||
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
|
||||
pane.add(opening_label, c);
|
||||
|
||||
cancel_button = new JButton("Cancel");
|
||||
cancel_button.addActionListener(this);
|
||||
cancel_button.setActionCommand("cancel");
|
||||
|
||||
c.gridy = 1;
|
||||
pane.add(cancel_button, c);
|
||||
pack();
|
||||
setLocationRelativeTo(owner);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean open_device() throws InterruptedException {
|
||||
|
||||
open_dialog dialog = new open_dialog(frame);
|
||||
|
||||
open_task open = new open_task(device, dialog);
|
||||
|
||||
link = dialog.do_open(open);
|
||||
|
||||
return link != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
76
altosuilib/AltosFlightInfoTableModel.java
Normal file
76
altosuilib/AltosFlightInfoTableModel.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
271
altosuilib/AltosFlightStatsTable.java
Normal file
271
altosuilib/AltosFlightStatsTable.java
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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 void set(String ... values) {
|
||||
for (int j = 0; j < values.length; j++)
|
||||
value[j].setText(values[j]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private FlightStat max_height_stat;
|
||||
private FlightStat max_speed_stat;
|
||||
private FlightStat max_accel_stat;
|
||||
private FlightStat boost_accel_stat;
|
||||
private FlightStat drogue_descent_stat;
|
||||
private FlightStat main_descent_stat;
|
||||
|
||||
public void set_values(AltosFlightStats stats) {
|
||||
if (max_height_stat != null && stats.max_height != AltosLib.MISSING) {
|
||||
max_height_stat.set(String.format("%6.1f m", stats.max_height),
|
||||
String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.max_height)));
|
||||
}
|
||||
if (max_speed_stat != null && stats.max_speed != AltosLib.MISSING) {
|
||||
max_speed_stat.set(String.format("%6.1f m/s", stats.max_speed),
|
||||
String.format("%5.0f fps", AltosConvert.mps_to_fps(stats.max_speed)),
|
||||
String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed)));
|
||||
}
|
||||
if (max_accel_stat != null && stats.max_acceleration != AltosLib.MISSING) {
|
||||
max_accel_stat.set(String.format("%6.1f m/s²", stats.max_acceleration),
|
||||
String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_acceleration)),
|
||||
String.format("%6.2f G", AltosConvert.meters_to_g(stats.max_acceleration)));
|
||||
}
|
||||
if (boost_accel_stat != null && stats.state_accel[AltosLib.ao_flight_boost] != AltosLib.MISSING) {
|
||||
boost_accel_stat.set(String.format("%6.1f 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("%6.2f G", AltosConvert.meters_to_g(stats.state_accel[AltosLib.ao_flight_boost])));
|
||||
}
|
||||
if (drogue_descent_stat != null && stats.state_speed[AltosLib.ao_flight_drogue] != AltosLib.MISSING) {
|
||||
drogue_descent_stat.set(String.format("%6.1f 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 (main_descent_stat != null && stats.state_speed[AltosLib.ao_flight_main] != AltosLib.MISSING) {
|
||||
main_descent_stat.set(String.format("%6.1f 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])));
|
||||
}
|
||||
}
|
||||
|
||||
public void set_stats(AltosFlightStats stats) {
|
||||
int y = 0;
|
||||
if (stats.serial != AltosLib.MISSING) {
|
||||
if (stats.product != null && stats.firmware_version != null)
|
||||
new FlightStat(layout, y++, "Device",
|
||||
stats.product,
|
||||
String.format("version %s", stats.firmware_version),
|
||||
String.format("serial %d", stats.serial));
|
||||
else
|
||||
new FlightStat(layout, y++, "Serial", String.format("%d", stats.serial));
|
||||
}
|
||||
if (stats.flight != AltosLib.MISSING)
|
||||
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) {
|
||||
max_height_stat = new FlightStat(layout, y++, "Maximum height",
|
||||
String.format("%6.1f 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("%6.1f m", stats.max_gps_height),
|
||||
String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.max_gps_height)));
|
||||
}
|
||||
if (stats.max_speed != AltosLib.MISSING) {
|
||||
max_speed_stat = new FlightStat(layout, y++, "Maximum speed",
|
||||
String.format("%6.1f m/s", stats.max_speed),
|
||||
String.format("%5.0f fps", AltosConvert.mps_to_fps(stats.max_speed)),
|
||||
String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed)));
|
||||
}
|
||||
if (stats.max_acceleration != AltosLib.MISSING)
|
||||
max_accel_stat = new FlightStat(layout, y++, "Maximum boost acceleration",
|
||||
String.format("%6.1f m/s²", stats.max_acceleration),
|
||||
String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_acceleration)),
|
||||
String.format("%6.2f G", AltosConvert.meters_to_g(stats.max_acceleration)));
|
||||
if (stats.state_accel[AltosLib.ao_flight_boost] != AltosLib.MISSING)
|
||||
boost_accel_stat = new FlightStat(layout, y++, "Average boost acceleration",
|
||||
String.format("%6.1f 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("%6.2f G", AltosConvert.meters_to_g(stats.state_accel[AltosLib.ao_flight_boost])));
|
||||
if (stats.state_time[AltosLib.ao_flight_boost] != 0 || stats.state_time[AltosLib.ao_flight_fast] != 0 || stats.state_time[AltosLib.ao_flight_coast] != 0) {
|
||||
|
||||
double boost_time = stats.state_time[AltosLib.ao_flight_boost];
|
||||
double fast_time = stats.state_time[AltosLib.ao_flight_fast];
|
||||
double coast_time = stats.state_time[AltosLib.ao_flight_coast];
|
||||
|
||||
if (fast_time > 0) {
|
||||
new FlightStat(layout, y++, "Ascent time",
|
||||
String.format("%6.1f s %s", boost_time,
|
||||
AltosLib.state_name(AltosLib.ao_flight_boost)),
|
||||
String.format("%6.1f s %s", fast_time,
|
||||
AltosLib.state_name(AltosLib.ao_flight_fast)),
|
||||
String.format("%6.1f s %s", coast_time,
|
||||
AltosLib.state_name(AltosLib.ao_flight_coast)));
|
||||
} else {
|
||||
new FlightStat(layout, y++, "Ascent time",
|
||||
String.format("%6.1f s %s", boost_time,
|
||||
AltosLib.state_name(AltosLib.ao_flight_boost)),
|
||||
String.format("%6.1f s %s", coast_time,
|
||||
AltosLib.state_name(AltosLib.ao_flight_coast)));
|
||||
}
|
||||
}
|
||||
if (stats.state_speed[AltosLib.ao_flight_drogue] != AltosLib.MISSING) {
|
||||
String label;
|
||||
|
||||
if (stats.state_speed[AltosLib.ao_flight_main] == AltosLib.MISSING)
|
||||
label = "Descent rate";
|
||||
else
|
||||
label = "Drogue descent rate";
|
||||
drogue_descent_stat = new FlightStat(layout, y++, label,
|
||||
String.format("%6.1f 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)
|
||||
main_descent_stat = new FlightStat(layout, y++, "Main descent rate",
|
||||
String.format("%6.1f 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_time[AltosLib.ao_flight_drogue] != 0 || stats.state_time[AltosLib.ao_flight_main] != 0) {
|
||||
double drogue_duration = stats.state_time[AltosLib.ao_flight_drogue];
|
||||
double main_duration = stats.state_time[AltosLib.ao_flight_main];
|
||||
double duration = drogue_duration + main_duration;
|
||||
|
||||
if (drogue_duration > 0 && main_duration > 0) {
|
||||
new FlightStat(layout, y++, "Descent time",
|
||||
String.format("%6.1f s %s", drogue_duration,
|
||||
AltosLib.state_name(AltosLib.ao_flight_drogue)),
|
||||
String.format("%6.1f s %s", main_duration,
|
||||
AltosLib.state_name(AltosLib.ao_flight_main)));
|
||||
} else if (duration > 0) {
|
||||
new FlightStat(layout, y++, "Descent time",
|
||||
String.format("%6.1f s", duration));
|
||||
}
|
||||
}
|
||||
if (stats.landed_time > stats.boost_time)
|
||||
new FlightStat(layout, y++, "Flight time",
|
||||
String.format("%6.1f s", stats.landed_time - stats.boost_time));
|
||||
if (stats.has_gps && stats.pad_lat != AltosLib.MISSING) {
|
||||
new FlightStat(layout, y++, "Pad location",
|
||||
pos(stats.pad_lat,"N","S"),
|
||||
pos(stats.pad_lon,"E","W"));
|
||||
}
|
||||
if (stats.has_gps && stats.lat != AltosLib.MISSING) {
|
||||
new FlightStat(layout, y++, "Last reported location",
|
||||
pos(stats.lat,"N","S"),
|
||||
pos(stats.lon,"E","W"));
|
||||
}
|
||||
}
|
||||
|
||||
public void tell_closing() {
|
||||
AltosUIPreferences.unregister_font_listener(this);
|
||||
}
|
||||
|
||||
public void filter_changed(AltosFlightStats stats) {
|
||||
set_values(stats);
|
||||
}
|
||||
|
||||
public AltosFlightStatsTable() {
|
||||
layout = new GridBagLayout();
|
||||
|
||||
setLayout(layout);
|
||||
|
||||
AltosUIPreferences.register_font_listener(this);
|
||||
}
|
||||
|
||||
public AltosFlightStatsTable(AltosFlightStats stats) {
|
||||
this();
|
||||
set_stats(stats);
|
||||
}
|
||||
}
|
||||
394
altosuilib/AltosGraph.java
Normal file
394
altosuilib/AltosGraph.java
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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.*;
|
||||
|
||||
public class AltosGraph extends AltosUIGraph {
|
||||
|
||||
/* These are in 'priority' order so that earlier ones get simpler line styles,
|
||||
* then they are grouped so that adjacent ones get sequential colors
|
||||
*/
|
||||
static final private AltosUILineStyle height_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle speed_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle accel_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle vert_accel_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle orient_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle azimuth_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle compass_color = new AltosUILineStyle();
|
||||
|
||||
static final private AltosUILineStyle gps_height_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle altitude_color = new AltosUILineStyle();
|
||||
|
||||
static final private AltosUILineStyle battery_voltage_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle pyro_voltage_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle drogue_voltage_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle main_voltage_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle igniter_marker_color = new AltosUILineStyle(1);
|
||||
|
||||
static final private AltosUILineStyle kalman_height_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle kalman_speed_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle kalman_accel_color = new AltosUILineStyle();
|
||||
|
||||
static final private AltosUILineStyle gps_nsat_color = new AltosUILineStyle ();
|
||||
static final private AltosUILineStyle gps_nsat_solution_color = new AltosUILineStyle ();
|
||||
static final private AltosUILineStyle gps_nsat_view_color = new AltosUILineStyle ();
|
||||
static final private AltosUILineStyle gps_course_color = new AltosUILineStyle ();
|
||||
static final private AltosUILineStyle gps_ground_speed_color = new AltosUILineStyle ();
|
||||
static final private AltosUILineStyle gps_speed_color = new AltosUILineStyle ();
|
||||
static final private AltosUILineStyle gps_climb_rate_color = new AltosUILineStyle ();
|
||||
static final private AltosUILineStyle gps_pdop_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle gps_hdop_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle gps_vdop_color = new AltosUILineStyle();
|
||||
|
||||
static final private AltosUILineStyle temperature_color = new AltosUILineStyle ();
|
||||
static final private AltosUILineStyle dbm_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle pressure_color = new AltosUILineStyle ();
|
||||
|
||||
static final private AltosUILineStyle state_color = new AltosUILineStyle(0);
|
||||
static final private AltosUILineStyle accel_along_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle accel_across_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle accel_through_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle gyro_roll_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle gyro_pitch_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle gyro_yaw_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle mag_along_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle mag_across_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle mag_through_color = new AltosUILineStyle();
|
||||
static final private AltosUILineStyle mag_total_color = new AltosUILineStyle();
|
||||
|
||||
static final private AltosUILineStyle motor_pressure_color = new AltosUILineStyle();
|
||||
|
||||
static AltosUnits dop_units = null;
|
||||
static AltosUnits tick_units = null;
|
||||
|
||||
AltosUIFlightSeries flight_series;
|
||||
|
||||
boolean enable_axis(int product_id, String label) {
|
||||
switch (product_id) {
|
||||
case AltosLib.product_easymotor:
|
||||
return(label.equals(AltosUIFlightSeries.motor_pressure_name) ||
|
||||
label.equals(AltosUIFlightSeries.accel_name));
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
AltosUITimeSeries[] setup(AltosFlightStats stats, AltosUIFlightSeries flight_series) {
|
||||
AltosCalData cal_data = flight_series.cal_data();
|
||||
int product_id = cal_data.device_type;
|
||||
|
||||
AltosUIAxis height_axis, speed_axis, accel_axis, voltage_axis, temperature_axis, nsat_axis, dbm_axis;
|
||||
AltosUIAxis pressure_axis, thrust_axis;
|
||||
AltosUIAxis gyro_axis, orient_axis, mag_axis;
|
||||
AltosUIAxis course_axis, dop_axis, tick_axis;
|
||||
AltosUIAxis motor_pressure_axis;
|
||||
|
||||
if (stats != null && stats.serial != AltosLib.MISSING && stats.product != null && stats.flight != AltosLib.MISSING)
|
||||
setName(String.format("%s %d flight %d\n", stats.product, stats.serial, stats.flight));
|
||||
|
||||
height_axis = newAxis("Height", AltosConvert.height, height_color);
|
||||
pressure_axis = newAxis("Pressure", AltosConvert.pressure, pressure_color, 0);
|
||||
speed_axis = newAxis("Speed", AltosConvert.speed, speed_color);
|
||||
thrust_axis = newAxis("Thrust", AltosConvert.force, accel_color);
|
||||
tick_axis = newAxis("Tick", tick_units, accel_color, 0);
|
||||
accel_axis = newAxis("Acceleration", AltosConvert.accel, accel_color);
|
||||
voltage_axis = newAxis("Voltage", AltosConvert.voltage, battery_voltage_color);
|
||||
temperature_axis = newAxis("Temperature", AltosConvert.temperature, temperature_color, 0);
|
||||
nsat_axis = newAxis("Satellites", null, gps_nsat_color,
|
||||
AltosUIAxis.axis_include_zero | AltosUIAxis.axis_integer);
|
||||
dbm_axis = newAxis("Signal Strength", null, dbm_color, 0);
|
||||
|
||||
gyro_axis = newAxis("Rotation Rate", AltosConvert.rotation_rate, gyro_roll_color, 0);
|
||||
orient_axis = newAxis("Angle", AltosConvert.orient, orient_color, 0);
|
||||
mag_axis = newAxis("Magnetic Field", AltosConvert.magnetic_field, mag_along_color, 0);
|
||||
course_axis = newAxis("Course", AltosConvert.orient, gps_course_color, 0);
|
||||
dop_axis = newAxis("Dilution of Precision", dop_units, gps_pdop_color, 0);
|
||||
|
||||
motor_pressure_axis = newAxis("Motor Pressure", AltosConvert.pressure, motor_pressure_color, 0);
|
||||
|
||||
flight_series.register_axis("default",
|
||||
speed_color,
|
||||
false,
|
||||
speed_axis);
|
||||
|
||||
flight_series.register_marker(AltosUIFlightSeries.state_name,
|
||||
state_color,
|
||||
enable_axis(product_id, AltosUIFlightSeries.state_name),
|
||||
plot,
|
||||
true);
|
||||
|
||||
flight_series.register_marker(AltosUIFlightSeries.pyro_fired_name,
|
||||
igniter_marker_color,
|
||||
true,
|
||||
plot,
|
||||
false);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.tick_name,
|
||||
accel_color,
|
||||
false,
|
||||
tick_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.accel_name,
|
||||
accel_color,
|
||||
enable_axis(product_id, AltosUIFlightSeries.accel_name),
|
||||
accel_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.vert_accel_name,
|
||||
vert_accel_color,
|
||||
enable_axis(product_id, AltosUIFlightSeries.vert_accel_name),
|
||||
accel_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.kalman_accel_name,
|
||||
kalman_accel_color,
|
||||
false,
|
||||
accel_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.rssi_name,
|
||||
dbm_color,
|
||||
false,
|
||||
dbm_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.speed_name,
|
||||
speed_color,
|
||||
enable_axis(product_id, AltosUIFlightSeries.speed_name),
|
||||
speed_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.kalman_speed_name,
|
||||
kalman_speed_color,
|
||||
enable_axis(product_id, AltosUIFlightSeries.kalman_speed_name),
|
||||
speed_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.pressure_name,
|
||||
pressure_color,
|
||||
false,
|
||||
pressure_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.height_name,
|
||||
height_color,
|
||||
enable_axis(product_id, AltosUIFlightSeries.height_name),
|
||||
height_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.altitude_name,
|
||||
altitude_color,
|
||||
false,
|
||||
height_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.kalman_height_name,
|
||||
kalman_height_color,
|
||||
false,
|
||||
height_axis);
|
||||
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.temperature_name,
|
||||
temperature_color,
|
||||
false,
|
||||
temperature_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.battery_voltage_name,
|
||||
battery_voltage_color,
|
||||
false,
|
||||
voltage_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.pyro_voltage_name,
|
||||
pyro_voltage_color,
|
||||
false,
|
||||
voltage_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.apogee_voltage_name,
|
||||
drogue_voltage_color,
|
||||
false,
|
||||
voltage_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.main_voltage_name,
|
||||
main_voltage_color,
|
||||
false,
|
||||
voltage_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.sats_in_view_name,
|
||||
gps_nsat_view_color,
|
||||
false,
|
||||
nsat_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.sats_in_soln_name,
|
||||
gps_nsat_solution_color,
|
||||
false,
|
||||
nsat_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gps_pdop_name,
|
||||
gps_pdop_color,
|
||||
false,
|
||||
dop_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gps_hdop_name,
|
||||
gps_hdop_color,
|
||||
false,
|
||||
dop_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gps_vdop_name,
|
||||
gps_vdop_color,
|
||||
false,
|
||||
dop_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gps_altitude_name,
|
||||
gps_height_color,
|
||||
false,
|
||||
height_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gps_height_name,
|
||||
gps_height_color,
|
||||
false,
|
||||
height_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gps_ground_speed_name,
|
||||
gps_ground_speed_color,
|
||||
false,
|
||||
speed_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gps_ascent_rate_name,
|
||||
gps_climb_rate_color,
|
||||
false,
|
||||
speed_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gps_course_name,
|
||||
gps_course_color,
|
||||
false,
|
||||
course_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gps_speed_name,
|
||||
gps_speed_color,
|
||||
false,
|
||||
speed_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.accel_along_name,
|
||||
accel_along_color,
|
||||
false,
|
||||
accel_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.accel_across_name,
|
||||
accel_across_color,
|
||||
false,
|
||||
accel_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.accel_through_name,
|
||||
accel_through_color,
|
||||
false,
|
||||
accel_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gyro_roll_name,
|
||||
gyro_roll_color,
|
||||
false,
|
||||
gyro_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gyro_pitch_name,
|
||||
gyro_pitch_color,
|
||||
false,
|
||||
gyro_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.gyro_yaw_name,
|
||||
gyro_yaw_color,
|
||||
false,
|
||||
gyro_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.mag_along_name,
|
||||
mag_along_color,
|
||||
false,
|
||||
mag_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.mag_across_name,
|
||||
mag_across_color,
|
||||
false,
|
||||
mag_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.mag_through_name,
|
||||
mag_through_color,
|
||||
false,
|
||||
mag_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.mag_total_name,
|
||||
mag_total_color,
|
||||
false,
|
||||
mag_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.orient_name,
|
||||
orient_color,
|
||||
false,
|
||||
orient_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.azimuth_name,
|
||||
azimuth_color,
|
||||
false,
|
||||
orient_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.compass_name,
|
||||
compass_color,
|
||||
false,
|
||||
orient_axis);
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.thrust_name,
|
||||
accel_color,
|
||||
enable_axis(product_id, AltosUIFlightSeries.thrust_name),
|
||||
thrust_axis);
|
||||
|
||||
for (int channel = 0; channel < 8; channel++) {
|
||||
flight_series.register_axis(flight_series.igniter_voltage_name(channel),
|
||||
new AltosUILineStyle(),
|
||||
false,
|
||||
voltage_axis);
|
||||
}
|
||||
|
||||
flight_series.register_axis(AltosUIFlightSeries.motor_pressure_name,
|
||||
motor_pressure_color,
|
||||
enable_axis(product_id, AltosUIFlightSeries.motor_pressure_name),
|
||||
motor_pressure_axis);
|
||||
|
||||
flight_series.check_axes();
|
||||
|
||||
return flight_series.series(cal_data);
|
||||
}
|
||||
|
||||
public void set_data(AltosFlightStats stats, AltosUIFlightSeries flight_series) {
|
||||
set_series(setup(stats, flight_series));
|
||||
}
|
||||
|
||||
public AltosGraph(AltosUIEnable enable) {
|
||||
super(enable, "Flight");
|
||||
}
|
||||
|
||||
public AltosGraph(AltosUIEnable enable, AltosFlightStats stats, AltosUIFlightSeries flight_series) {
|
||||
this(enable);
|
||||
this.flight_series = flight_series;
|
||||
set_series(setup(stats, flight_series));
|
||||
}
|
||||
}
|
||||
299
altosuilib/AltosInfoTable.java
Normal file
299
altosuilib/AltosInfoTable.java
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosInfoTable extends JTable implements AltosFlightDisplay, HierarchyListener {
|
||||
private AltosFlightInfoTableModel model;
|
||||
|
||||
static final int info_columns = 4;
|
||||
static final int info_rows = 18;
|
||||
|
||||
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) {
|
||||
|
||||
AltosCalData cal_data = state.cal_data();
|
||||
|
||||
if (!isShowing()) {
|
||||
last_state = state;
|
||||
last_listener_state = listener_state;
|
||||
return;
|
||||
}
|
||||
|
||||
reset();
|
||||
if (state != null) {
|
||||
if (cal_data.device_type != AltosLib.MISSING)
|
||||
info_add_row(0, "Device", "%s", AltosLib.product_name(cal_data.device_type));
|
||||
else if (cal_data.product != null)
|
||||
info_add_row(0, "Device", "%s", cal_data.product);
|
||||
if (state.tick() != AltosLib.MISSING)
|
||||
info_add_row(0, "Tick", "%6d", state.tick());
|
||||
if (state.altitude() != AltosLib.MISSING)
|
||||
info_add_row(0, "Altitude", "%6.0f m", state.altitude());
|
||||
if (cal_data.ground_altitude != AltosLib.MISSING)
|
||||
info_add_row(0, "Pad altitude", "%6.0f m", cal_data.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 m", state.gps.alt);
|
||||
if (state.gps_height != AltosLib.MISSING)
|
||||
info_add_row(1, "GPS height", "%8.1f m", state.gps_height);
|
||||
|
||||
if (state.gps.ground_speed != AltosLib.MISSING && state.gps.course != AltosLib.MISSING)
|
||||
info_add_row(1, "GPS ground speed", "%6.1f m/s %3d°",
|
||||
state.gps.ground_speed,
|
||||
state.gps.course);
|
||||
if (state.gps.climb_rate != AltosLib.MISSING)
|
||||
info_add_row(1, "GPS climb rate", "%6.1f m/s",
|
||||
state.gps.climb_rate);
|
||||
|
||||
if (state.gps.h_error != AltosLib.MISSING && state.gps.v_error != AltosLib.MISSING)
|
||||
info_add_row(1, "GPS error", "%6.0f m(h)%6.0f m(v)",
|
||||
state.gps.h_error, state.gps.v_error);
|
||||
if (state.gps.pdop != AltosLib.MISSING &&
|
||||
state.gps.hdop != AltosLib.MISSING &&
|
||||
state.gps.vdop != AltosLib.MISSING)
|
||||
info_add_row(1, "GPS dop", "%3.1fp/%3.1fh/%3.1fv",
|
||||
state.gps.pdop,
|
||||
state.gps.hdop,
|
||||
state.gps.vdop);
|
||||
|
||||
if (state.npad > 0) {
|
||||
if (state.from_pad != null) {
|
||||
info_add_row(1, "Ground pad dist", "%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.gps_ground_altitude());
|
||||
}
|
||||
if (state.gps.year != AltosLib.MISSING)
|
||||
info_add_row(2, "GPS date", "%04d-%02d-%02d",
|
||||
state.gps.year,
|
||||
state.gps.month,
|
||||
state.gps.day);
|
||||
if (state.gps.hour != AltosLib.MISSING)
|
||||
info_add_row(2, "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state != null && state.accel_along() != AltosLib.MISSING) {
|
||||
info_add_row(3, "Accel along", "%8.1f m/s²", state.accel_along());
|
||||
info_add_row(3, "Accel across", "%8.1f m/s²", state.accel_across());
|
||||
info_add_row(3, "Accel through", "%8.1f m/s²", state.accel_through());
|
||||
}
|
||||
if (state != null && state.gyro_roll() != AltosLib.MISSING) {
|
||||
info_add_row(3, "Gyro roll", "%8.1f °/s", state.gyro_roll());
|
||||
info_add_row(3, "Gyro pitch", "%8.1f °/s", state.gyro_pitch());
|
||||
info_add_row(3, "Gyro yaw", "%8.1f °/s", state.gyro_yaw());
|
||||
}
|
||||
if (state != null && state.mag_along() != AltosLib.MISSING) {
|
||||
/* Report mag in nanoteslas (1 G = 100000 nT (or γ)) */
|
||||
info_add_row(3, "Mag along", "%8.1f µT", state.mag_along() * 100.0);
|
||||
info_add_row(3, "Mag across", "%8.1f µT", state.mag_across() * 100.0);
|
||||
info_add_row(3, "Mag Through", "%8.1f µT", state.mag_through() * 100.0);
|
||||
info_add_row(3, "Mag Bearing", "%8.1f°", Math.atan2(state.mag_across(), state.mag_through()) * 180/Math.PI);
|
||||
}
|
||||
|
||||
if (state != null && state.igniter_voltage != null) {
|
||||
for (int i = 0; i < state.igniter_voltage.length; i++)
|
||||
info_add_row(3, AltosLib.igniter_name(i), "%9.2f V", state.igniter_voltage[i]);
|
||||
}
|
||||
|
||||
info_finish();
|
||||
}
|
||||
}
|
||||
46
altosuilib/AltosLed.java
Normal file
46
altosuilib/AltosLed.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
66
altosuilib/AltosLights.java
Normal file
66
altosuilib/AltosLights.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
23
altosuilib/AltosPositionListener.java
Normal file
23
altosuilib/AltosPositionListener.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
public interface AltosPositionListener {
|
||||
public void position_changed(int position);
|
||||
}
|
||||
230
altosuilib/AltosRomconfigUI.java
Normal file
230
altosuilib/AltosRomconfigUI.java
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosRomconfigUI
|
||||
extends AltosUIDialog
|
||||
implements ActionListener
|
||||
{
|
||||
Container pane;
|
||||
Box box;
|
||||
JLabel product_label;
|
||||
JLabel serial_label;
|
||||
JLabel radio_calibration_label;
|
||||
|
||||
JLabel product_value;
|
||||
JFrame owner;
|
||||
JTextField serial_value;
|
||||
JTextField radio_calibration_value;
|
||||
|
||||
JButton ok;
|
||||
JButton cancel;
|
||||
|
||||
/* Build the UI using a grid bag */
|
||||
public AltosRomconfigUI(JFrame frame, AltosRomconfig config) {
|
||||
super (frame, "Configure Rom Values", true);
|
||||
|
||||
owner = frame;
|
||||
GridBagConstraints c;
|
||||
|
||||
Insets il = new Insets(4,4,4,4);
|
||||
Insets ir = new Insets(4,4,4,4);
|
||||
|
||||
pane = getScrollablePane();
|
||||
pane.setLayout(new GridBagLayout());
|
||||
|
||||
int y = 0;
|
||||
|
||||
/* Product name */
|
||||
/* Serial */
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 0; c.gridy = y;
|
||||
c.gridwidth = 3;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = il;
|
||||
product_label = new JLabel("Product:");
|
||||
pane.add(product_label, c);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 3; c.gridy = y;
|
||||
c.gridwidth = 3;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = ir;
|
||||
String product = "unknown";
|
||||
if (config != null)
|
||||
product = config.usb_product;
|
||||
product_value = new JLabel(product);
|
||||
pane.add(product_value, c);
|
||||
|
||||
y++;
|
||||
|
||||
/* Serial */
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 0; c.gridy = y;
|
||||
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 = y;
|
||||
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);
|
||||
|
||||
y++;
|
||||
|
||||
if (config == null || AltosLib.has_radio(config.usb_id.pid)) {
|
||||
/* Radio calibration value */
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 0; c.gridy = y;
|
||||
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 = y;
|
||||
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);
|
||||
|
||||
y++;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 0; c.gridy = y;
|
||||
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 = y;
|
||||
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");
|
||||
|
||||
y++;
|
||||
|
||||
pack();
|
||||
setLocationRelativeTo(owner);
|
||||
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() {
|
||||
if (radio_calibration_value == null)
|
||||
return 0;
|
||||
|
||||
return Integer.parseInt(radio_calibration_value.getText());
|
||||
}
|
||||
|
||||
void set_radio_calibration(int calibration) {
|
||||
if (radio_calibration_value == null)
|
||||
return;
|
||||
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();
|
||||
}
|
||||
}
|
||||
677
altosuilib/AltosScanUI.java
Normal file
677
altosuilib/AltosScanUI.java
Normal file
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
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_14.*;
|
||||
|
||||
class AltosScanResult {
|
||||
String callsign;
|
||||
int serial;
|
||||
int flight;
|
||||
AltosFrequency frequency;
|
||||
int telemetry;
|
||||
int rate;
|
||||
|
||||
boolean interrupted = false;
|
||||
|
||||
public String toString() {
|
||||
return String.format("%-9.9s serial %-4d flight %-4d (%s %s %d)",
|
||||
callsign, serial, flight,
|
||||
frequency.toShortString(),
|
||||
AltosLib.telemetry_name(telemetry),
|
||||
AltosLib.ao_telemetry_rate_values[rate]);
|
||||
}
|
||||
|
||||
public String toShortString() {
|
||||
return String.format("%s %d %d %7.3f %d %d",
|
||||
callsign, serial, flight, frequency, telemetry, rate);
|
||||
}
|
||||
|
||||
public AltosScanResult(String in_callsign, int in_serial,
|
||||
int in_flight, AltosFrequency in_frequency,
|
||||
int in_telemetry,
|
||||
int in_rate) {
|
||||
callsign = in_callsign;
|
||||
serial = in_serial;
|
||||
flight = in_flight;
|
||||
frequency = in_frequency;
|
||||
telemetry = in_telemetry;
|
||||
rate = in_rate;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return serial ^ frequency.hashCode() ^ telemetry ^ rate;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null)
|
||||
return false;
|
||||
if (!(o instanceof AltosScanResult))
|
||||
return false;
|
||||
AltosScanResult other = (AltosScanResult) o;
|
||||
return (serial == other.serial &&
|
||||
frequency.equals(other.frequency) &&
|
||||
telemetry == other.telemetry &&
|
||||
rate == other.rate);
|
||||
}
|
||||
|
||||
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 JLabel rate_label;
|
||||
private JButton cancel_button;
|
||||
private JButton monitor_button;
|
||||
private JCheckBox[] telemetry_boxes;
|
||||
private JCheckBox[] rate_boxes;
|
||||
javax.swing.Timer timer;
|
||||
AltosScanResults results = new AltosScanResults();
|
||||
|
||||
int telemetry;
|
||||
boolean select_telemetry = false;
|
||||
|
||||
int rate;
|
||||
boolean select_rate = false;
|
||||
|
||||
final static int timeout = 1200;
|
||||
TelemetryHandler handler;
|
||||
Thread thread;
|
||||
AltosFrequency[] frequencies;
|
||||
int frequency_index;
|
||||
int packet_count;
|
||||
int tick_count;
|
||||
|
||||
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;
|
||||
packet_count++;
|
||||
AltosCalData cal_data = state.cal_data();
|
||||
if (cal_data.flight != AltosLib.MISSING) {
|
||||
final AltosScanResult result = new AltosScanResult(cal_data.callsign,
|
||||
cal_data.serial,
|
||||
cal_data.flight,
|
||||
frequencies[frequency_index],
|
||||
telemetry,
|
||||
rate);
|
||||
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)));
|
||||
if (select_rate)
|
||||
rate_label.setText(String.format("Rate: %d baud", AltosLib.ao_telemetry_rate_values[rate]));
|
||||
}
|
||||
|
||||
void set_telemetry() {
|
||||
reader.set_telemetry(telemetry);
|
||||
}
|
||||
|
||||
void set_rate() {
|
||||
reader.set_telemetry_rate(rate);
|
||||
}
|
||||
|
||||
void set_frequency() throws InterruptedException, TimeoutException {
|
||||
reader.set_frequency(frequencies[frequency_index].frequency);
|
||||
reader.reset();
|
||||
}
|
||||
|
||||
void next() throws InterruptedException, TimeoutException {
|
||||
reader.set_monitor(false);
|
||||
|
||||
if (select_rate) {
|
||||
boolean wrapped = false;
|
||||
do {
|
||||
++rate;
|
||||
if (rate > AltosLib.ao_telemetry_rate_max) {
|
||||
wrapped = true;
|
||||
rate = 0;
|
||||
}
|
||||
} while (!rate_boxes[rate].isSelected());
|
||||
set_rate();
|
||||
if (!wrapped) {
|
||||
set_label();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (select_telemetry) {
|
||||
boolean wrapped = false;
|
||||
do {
|
||||
++telemetry;
|
||||
if (telemetry > AltosLib.ao_telemetry_max) {
|
||||
wrapped = true;
|
||||
telemetry = AltosLib.ao_telemetry_min;
|
||||
}
|
||||
} while (!telemetry_boxes[telemetry - AltosLib.ao_telemetry_min].isSelected());
|
||||
set_telemetry();
|
||||
if (!wrapped) {
|
||||
set_label();
|
||||
return;
|
||||
}
|
||||
}
|
||||
packet_count = 0;
|
||||
tick_count = 0;
|
||||
++frequency_index;
|
||||
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 {
|
||||
++tick_count;
|
||||
if (packet_count == 0 || tick_count > 5)
|
||||
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("rate")) {
|
||||
int k;
|
||||
int scanning_rate = 0;
|
||||
for (k = 0; k <= AltosLib.ao_telemetry_rate_max; k++) {
|
||||
if (rate_boxes[k].isSelected())
|
||||
scanning_rate |= (1 << k);
|
||||
}
|
||||
if (scanning_rate == 0) {
|
||||
scanning_rate = (1 << 0);
|
||||
rate_boxes[0].setSelected(true);
|
||||
}
|
||||
AltosUIPreferences.set_scanning_telemetry_rate(scanning_rate);
|
||||
}
|
||||
|
||||
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_telemetry_rate(r.rate);
|
||||
reader.set_frequency(r.frequency.frequency);
|
||||
reader.save_frequency();
|
||||
reader.save_telemetry();
|
||||
reader.save_telemetry_rate();
|
||||
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();
|
||||
set_rate();
|
||||
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) {
|
||||
super(in_owner, "Scan Telemetry", false);
|
||||
|
||||
owner = in_owner;
|
||||
select_telemetry = in_select_telemetry;
|
||||
select_rate = true;
|
||||
|
||||
frequencies = AltosUIPreferences.common_frequencies();
|
||||
frequency_index = 0;
|
||||
|
||||
telemetry = AltosLib.ao_telemetry_standard;
|
||||
rate = 0;
|
||||
|
||||
if (!open())
|
||||
return;
|
||||
|
||||
Container pane = getScrollablePane();
|
||||
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_label.setPreferredSize(new Dimension(100, 16));
|
||||
telemetry = AltosLib.ao_telemetry_min;
|
||||
} else {
|
||||
telemetry = AltosLib.ao_telemetry_standard;
|
||||
}
|
||||
|
||||
if (select_rate) {
|
||||
rate_label = new JLabel("");
|
||||
rate_label.setPreferredSize(new Dimension(100, 16));
|
||||
}
|
||||
|
||||
set_label();
|
||||
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = i;
|
||||
c.weightx = 0;
|
||||
c.weighty = 0;
|
||||
|
||||
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_rate = 3;
|
||||
int y_offset_telem = 3;
|
||||
|
||||
int check_x = 0;
|
||||
|
||||
if (select_rate && select_telemetry)
|
||||
c.gridwidth = 1;
|
||||
|
||||
if (select_rate) {
|
||||
c.gridy = 2;
|
||||
c.gridx = check_x++;
|
||||
pane.add(rate_label, c);
|
||||
|
||||
int scanning_rate = AltosUIPreferences.scanning_telemetry_rate();
|
||||
rate_boxes = new JCheckBox[AltosLib.ao_telemetry_rate_max + 1];
|
||||
for (int k = 0; k <= AltosLib.ao_telemetry_rate_max; k++) {
|
||||
rate_boxes[k] = new JCheckBox(String.format("%d baud", AltosLib.ao_telemetry_rate_values[k]));
|
||||
c.gridy = y_offset_rate + k;
|
||||
pane.add(rate_boxes[k], c);
|
||||
rate_boxes[k].setActionCommand("rate");
|
||||
rate_boxes[k].addActionListener(this);
|
||||
rate_boxes[k].setSelected((scanning_rate & (1 << k)) != 0);
|
||||
}
|
||||
y_offset_rate += AltosLib.ao_telemetry_rate_max + 1;
|
||||
}
|
||||
|
||||
if (select_telemetry) {
|
||||
c.gridy = 2;
|
||||
c.gridx = check_x++;
|
||||
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 = y_offset_telem + 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_telem += (AltosLib.ao_telemetry_max - AltosLib.ao_telemetry_min + 1);
|
||||
}
|
||||
|
||||
int y_offset = Math.max(y_offset_rate, y_offset_telem);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
209
altosuilib/AltosSerial.java
Normal file
209
altosuilib/AltosSerial.java
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, 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_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
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();
|
||||
}
|
||||
}
|
||||
27
altosuilib/AltosSerialInUseException.java
Normal file
27
altosuilib/AltosSerialInUseException.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
public class AltosSerialInUseException extends Exception {
|
||||
public AltosDevice device;
|
||||
|
||||
public AltosSerialInUseException (AltosDevice in_device) {
|
||||
device = in_device;
|
||||
}
|
||||
}
|
||||
20
altosuilib/AltosShapeListener.java
Normal file
20
altosuilib/AltosShapeListener.java
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright © 2017 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, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
public interface AltosShapeListener {
|
||||
void set_shapes_visible(boolean visible);
|
||||
void set_line_width(float width);
|
||||
}
|
||||
253
altosuilib/AltosUIAccelCal.java
Normal file
253
altosuilib/AltosUIAccelCal.java
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright © 2017 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.beans.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosUIAccelCal
|
||||
extends AltosUIDialog
|
||||
implements AltosAccelCalListener, ActionListener
|
||||
{
|
||||
Frame owner;
|
||||
AltosLink link;
|
||||
AltosAccelCal cal;
|
||||
AltosConfigValues config_values;
|
||||
Thread thread;
|
||||
Container pane;
|
||||
JTextField message;
|
||||
JButton antenna_up;
|
||||
JButton antenna_down;
|
||||
JButton ok;
|
||||
JButton cancel;
|
||||
boolean success;
|
||||
int accel_plus, accel_minus;
|
||||
|
||||
private void make_visible() {
|
||||
pack();
|
||||
cal.start();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
public boolean doit() {
|
||||
success = false;
|
||||
make_visible();
|
||||
return success;
|
||||
}
|
||||
|
||||
public int accel_cal_plus() {
|
||||
if (success)
|
||||
return accel_plus;
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public int accel_cal_minus() {
|
||||
if (success)
|
||||
return accel_minus;
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
private void setDefaultButton(JButton button) {
|
||||
this.getRootPane().setDefaultButton(button);
|
||||
}
|
||||
|
||||
/* AltosAccelCalListener interface */
|
||||
public void set_thread(AltosAccelCal cal, Thread thread) {
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
public void set_phase(AltosAccelCal cal, final int phase) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
switch (phase) {
|
||||
case AltosAccelCal.phase_antenna_up:
|
||||
message.setText(String.format ("Orient antenna upwards and click on %s", up_msg()));
|
||||
antenna_up.setEnabled(true);
|
||||
setDefaultButton(antenna_up);
|
||||
antenna_down.setEnabled(false);
|
||||
ok.setEnabled(false);
|
||||
break;
|
||||
case AltosAccelCal.phase_antenna_down:
|
||||
message.setText(String.format("Orient antenna downwards and click on %s", down_msg()));
|
||||
antenna_up.setEnabled(false);
|
||||
antenna_down.setEnabled(true);
|
||||
setDefaultButton(antenna_down);
|
||||
ok.setEnabled(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void cal_done(AltosAccelCal cal, int plus, int minus) {
|
||||
accel_plus = plus;
|
||||
accel_minus = minus;
|
||||
success = true;
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
message.setText(String.format("Calibration succeeded, plus %d minus %d, press OK to continue", accel_plus, accel_minus));
|
||||
antenna_up.setEnabled(false);
|
||||
antenna_down.setEnabled(false);
|
||||
ok.setEnabled(true);
|
||||
setDefaultButton(ok);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void message(AltosAccelCal cal, final String msg) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
message.setText(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void error(AltosAccelCal cal, String msg) {
|
||||
message(cal, msg);
|
||||
}
|
||||
|
||||
/* ActionListener interface */
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String cmd = e.getActionCommand();
|
||||
|
||||
if ("up".equals(cmd)) {
|
||||
cal.signal(true);
|
||||
antenna_up.setEnabled(false);
|
||||
} else if ("down".equals(cmd)) {
|
||||
cal.signal(true);
|
||||
antenna_down.setEnabled(false);
|
||||
this.setDefaultButton(antenna_down);
|
||||
} else if ("ok".equals(cmd)) {
|
||||
cal.signal(true);
|
||||
this.setVisible(false);
|
||||
if (success) {
|
||||
config_values.set_accel_cal(accel_plus, accel_minus);
|
||||
config_values.set_dirty();
|
||||
}
|
||||
try {
|
||||
cal.abort();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
} else if ("cancel".equals(cmd)) {
|
||||
cal.signal(false);
|
||||
this.setVisible(false);
|
||||
try {
|
||||
cal.abort();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String up_msg() { return config_values.has_radio() ? "Antenna Up" : "Beeper Up"; }
|
||||
public String down_msg() { return config_values.has_radio() ? "Antenna Down" : "Beeper Down"; }
|
||||
|
||||
public AltosUIAccelCal(Frame owner, AltosLink link, AltosConfigValues config_values) {
|
||||
super(owner, "Calibrate Accelerometer", true);
|
||||
|
||||
this.owner = owner;
|
||||
this.link = link;
|
||||
this.config_values = config_values;
|
||||
|
||||
pane = getScrollablePane();
|
||||
pane.setLayout(new GridBagLayout());
|
||||
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.insets = new Insets(4,4,4,4);
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
c.gridx = x;
|
||||
c.gridy = y;
|
||||
c.gridwidth = 4;
|
||||
c.gridheight = 1;
|
||||
c.weightx = 0;
|
||||
c.weighty = 0;
|
||||
message = new JTextField(64);
|
||||
pane.add(message, c);
|
||||
|
||||
y++; x = 0;
|
||||
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
c.gridx = x;
|
||||
c.gridy = y;
|
||||
c.gridwidth = 1;
|
||||
c.gridheight = 1;
|
||||
c.weightx = 0;
|
||||
c.weighty = 0;
|
||||
antenna_up = new JButton(up_msg());
|
||||
antenna_up.setActionCommand("up");
|
||||
antenna_up.setEnabled(false);
|
||||
antenna_up.addActionListener(this);
|
||||
pane.add(antenna_up, c);
|
||||
|
||||
x++;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
c.gridx = x;
|
||||
c.gridy = y;
|
||||
c.gridwidth = 1;
|
||||
c.gridheight = 1;
|
||||
c.weightx = 0;
|
||||
c.weighty = 0;
|
||||
antenna_down = new JButton(down_msg());
|
||||
antenna_down.setActionCommand("down");
|
||||
antenna_down.setEnabled(false);
|
||||
antenna_down.addActionListener(this);
|
||||
pane.add(antenna_down, c);
|
||||
|
||||
x++;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
c.gridx = x;
|
||||
c.gridy = y;
|
||||
c.gridwidth = 1;
|
||||
c.gridheight = 1;
|
||||
c.weightx = 0;
|
||||
c.weighty = 0;
|
||||
ok = new JButton("OK");
|
||||
ok.setActionCommand("ok");
|
||||
ok.setEnabled(false);
|
||||
ok.addActionListener(this);
|
||||
pane.add(ok, c);
|
||||
|
||||
x++;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
c.gridx = x;
|
||||
c.gridy = y;
|
||||
c.gridwidth = 1;
|
||||
c.gridheight = 1;
|
||||
c.weightx = 0;
|
||||
c.weighty = 0;
|
||||
cancel = new JButton("Cancel");
|
||||
cancel.setActionCommand("cancel");
|
||||
cancel.setEnabled(true);
|
||||
cancel.addActionListener(this);
|
||||
pane.add(cancel, c);
|
||||
|
||||
cal = new AltosAccelCal(this.link, this);
|
||||
}
|
||||
}
|
||||
103
altosuilib/AltosUIAxis.java
Normal file
103
altosuilib/AltosUIAxis.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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.*;
|
||||
|
||||
public class AltosUIAxis extends NumberAxis {
|
||||
String label;
|
||||
AltosUnits units;
|
||||
AltosUILineStyle line_style;
|
||||
int ref;
|
||||
int visible;
|
||||
int index;
|
||||
|
||||
public final static int axis_integer = 1;
|
||||
public final static int axis_include_zero = 2;
|
||||
|
||||
public final static int axis_default = axis_include_zero;
|
||||
|
||||
public void set_units() {
|
||||
if (units != null) {
|
||||
String u = units.parse_units();
|
||||
if (u != null) {
|
||||
setLabel(String.format("%s (%s)", label, u));
|
||||
return;
|
||||
}
|
||||
}
|
||||
setLabel(label);
|
||||
}
|
||||
|
||||
public void set_enable(boolean enable) {
|
||||
if (enable) {
|
||||
visible++;
|
||||
if (visible > ref)
|
||||
System.out.printf("too many visible\n");
|
||||
} else {
|
||||
visible--;
|
||||
if (visible < 0)
|
||||
System.out.printf("too few visible\n");
|
||||
}
|
||||
setVisible(visible > 0);
|
||||
if (enable)
|
||||
autoAdjustRange();
|
||||
}
|
||||
|
||||
public void ref(boolean enable) {
|
||||
++ref;
|
||||
if (enable) {
|
||||
++visible;
|
||||
setVisible(visible > 0);
|
||||
}
|
||||
}
|
||||
|
||||
public AltosUIAxis(String label, AltosUnits units, AltosUILineStyle line_style, int index, int flags) {
|
||||
this.label = label;
|
||||
this.units = units;
|
||||
this.line_style = line_style;
|
||||
this.index = index;
|
||||
this.visible = 0;
|
||||
this.ref = 0;
|
||||
setLabelPaint(line_style.color);
|
||||
setTickLabelPaint(line_style.color);
|
||||
setVisible(false);
|
||||
if ((flags & axis_integer) != 0)
|
||||
setStandardTickUnits(NumberAxis.createIntegerTickUnits());
|
||||
setAutoRangeIncludesZero((flags & axis_include_zero) != 0);
|
||||
}
|
||||
|
||||
public AltosUIAxis(String label, AltosUnits units, AltosUILineStyle line_style, int index) {
|
||||
this(label, units, line_style, index, axis_default);
|
||||
}
|
||||
}
|
||||
308
altosuilib/AltosUIConfigure.java
Normal file
308
altosuilib/AltosUIConfigure.java
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.beans.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
class DelegatingRenderer implements ListCellRenderer<Object> {
|
||||
|
||||
// ...
|
||||
public static void install(JComboBox<Object> comboBox) {
|
||||
DelegatingRenderer renderer = new DelegatingRenderer(comboBox);
|
||||
renderer.initialise();
|
||||
comboBox.setRenderer(renderer);
|
||||
}
|
||||
|
||||
// ...
|
||||
private final JComboBox comboBox;
|
||||
|
||||
// ...
|
||||
private ListCellRenderer<? super Object> delegate;
|
||||
|
||||
// ...
|
||||
private DelegatingRenderer(JComboBox comboBox) {
|
||||
this.comboBox = comboBox;
|
||||
}
|
||||
|
||||
// ...
|
||||
private void initialise() {
|
||||
JComboBox<Object> c = new JComboBox<Object>();
|
||||
delegate = c.getRenderer();
|
||||
comboBox.addPropertyChangeListener("UI", new PropertyChangeListener() {
|
||||
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
delegate = new JComboBox<Object>().getRenderer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ...
|
||||
public Component getListCellRendererComponent(JList<?> list,
|
||||
Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
|
||||
return delegate.getListCellRendererComponent(list,
|
||||
((UIManager.LookAndFeelInfo)value).getName(),
|
||||
index, isSelected, cellHasFocus);
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosUIConfigure
|
||||
extends AltosUIDialog
|
||||
{
|
||||
public JFrame owner;
|
||||
public Container pane;
|
||||
|
||||
public int row;
|
||||
|
||||
final static String[] font_size_names = { "Small", "Medium", "Large", "Huge" };
|
||||
|
||||
public GridBagConstraints constraints (int x, int width, int fill) {
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
Insets insets = new Insets(4, 4, 4, 4);
|
||||
|
||||
c.insets = insets;
|
||||
c.fill = fill;
|
||||
if (width == 3)
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
else if (x == 2)
|
||||
c.anchor = GridBagConstraints.EAST;
|
||||
else
|
||||
c.anchor = GridBagConstraints.WEST;
|
||||
c.gridx = x;
|
||||
c.gridwidth = width;
|
||||
c.gridy = row;
|
||||
return c;
|
||||
}
|
||||
|
||||
public GridBagConstraints constraints(int x, int width) {
|
||||
return constraints(x, width, GridBagConstraints.NONE);
|
||||
}
|
||||
|
||||
public void add_voice() {
|
||||
}
|
||||
|
||||
public void add_log_dir() {
|
||||
/* Log directory settings */
|
||||
pane.add(new JLabel("Log Directory"), constraints(0, 1));
|
||||
|
||||
final JButton configure_log = new JButton(AltosUIPreferences.logdir().getPath());
|
||||
configure_log.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
AltosUIPreferences.ConfigureLog();
|
||||
configure_log.setText(AltosUIPreferences.logdir().getPath());
|
||||
}
|
||||
});
|
||||
pane.add(configure_log, constraints(1, 2));
|
||||
configure_log.setToolTipText("Which directory flight logs are stored in");
|
||||
row++;
|
||||
}
|
||||
|
||||
public void add_callsign() {
|
||||
}
|
||||
|
||||
public void add_units() {
|
||||
/* Imperial units setting */
|
||||
pane.add(new JLabel("Imperial Units"), constraints(0, 1));
|
||||
|
||||
JRadioButton imperial_units = new JRadioButton("Enable", AltosUIPreferences.imperial_units());
|
||||
imperial_units.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JRadioButton item = (JRadioButton) e.getSource();
|
||||
boolean enabled = item.isSelected();
|
||||
AltosUIPreferences.set_imperial_units(enabled);
|
||||
}
|
||||
});
|
||||
imperial_units.setToolTipText("Use Imperial units instead of metric");
|
||||
pane.add(imperial_units, constraints(1, 2));
|
||||
row++;
|
||||
}
|
||||
|
||||
public void add_font_size() {
|
||||
/* Font size setting */
|
||||
pane.add(new JLabel("Font size"), constraints(0, 1));
|
||||
|
||||
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() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int size = font_size_value.getSelectedIndex() + AltosUILib.font_size_small;
|
||||
|
||||
AltosUIPreferences.set_font_size(size);
|
||||
}
|
||||
});
|
||||
pane.add(font_size_value, constraints(1, 2, GridBagConstraints.BOTH));
|
||||
font_size_value.setToolTipText("Font size used in telemetry window");
|
||||
row++;
|
||||
}
|
||||
|
||||
public void add_look_and_feel() {
|
||||
/* Look & Feel setting */
|
||||
pane.add(new JLabel("Look & feel"), constraints(0, 1));
|
||||
|
||||
/*
|
||||
class LookAndFeelRenderer extends BasicComboBoxRenderer implements ListCellRenderer {
|
||||
|
||||
public LookAndFeelRenderer() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Component getListCellRendererComponent(
|
||||
JList list,
|
||||
Object value,
|
||||
int index,
|
||||
boolean isSelected,
|
||||
boolean cellHasFocus)
|
||||
{
|
||||
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
setText(((UIManager.LookAndFeelInfo) value).getName());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
final UIManager.LookAndFeelInfo[] look_and_feels = UIManager.getInstalledLookAndFeels();
|
||||
|
||||
final JComboBox<Object> look_and_feel_value = new JComboBox<Object>(look_and_feels);
|
||||
|
||||
DelegatingRenderer.install(look_and_feel_value);
|
||||
|
||||
String look_and_feel = AltosUIPreferences.look_and_feel();
|
||||
for (int i = 0; i < look_and_feels.length; i++)
|
||||
if (look_and_feel.equals(look_and_feels[i].getClassName()))
|
||||
look_and_feel_value.setSelectedIndex(i);
|
||||
|
||||
look_and_feel_value.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int id = look_and_feel_value.getSelectedIndex();
|
||||
|
||||
AltosUIPreferences.set_look_and_feel(look_and_feels[id].getClassName());
|
||||
}
|
||||
});
|
||||
pane.add(look_and_feel_value, constraints(1, 2, GridBagConstraints.BOTH));
|
||||
look_and_feel_value.setToolTipText("Look&feel used for new windows");
|
||||
row++;
|
||||
}
|
||||
|
||||
public void add_position () {
|
||||
}
|
||||
|
||||
public void add_serial_debug() {
|
||||
/* Serial debug setting */
|
||||
pane.add(new JLabel("Serial Debug"), constraints(0, 1));
|
||||
|
||||
JRadioButton serial_debug = new JRadioButton("Enable", AltosUIPreferences.serial_debug());
|
||||
serial_debug.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JRadioButton item = (JRadioButton) e.getSource();
|
||||
boolean enabled = item.isSelected();
|
||||
AltosUIPreferences.set_serial_debug(enabled);
|
||||
}
|
||||
});
|
||||
serial_debug.setToolTipText("Enable/Disable USB I/O getting sent to the console");
|
||||
pane.add(serial_debug, constraints(1,2));
|
||||
row++;
|
||||
}
|
||||
|
||||
static final Integer map_caches[] = { 9, 25, 100 };
|
||||
|
||||
public void add_map_cache() {
|
||||
pane.add(new JLabel("Map Cache Size"), constraints(0, 1));
|
||||
|
||||
final JComboBox<Integer> map_cache = new JComboBox<Integer>(map_caches);
|
||||
|
||||
map_cache.setEditable(true);
|
||||
map_cache.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
try {
|
||||
int size = (Integer) (map_cache.getSelectedItem());
|
||||
|
||||
AltosUIPreferences.set_map_cache(size);
|
||||
} catch (ClassCastException ce) {
|
||||
map_cache.setSelectedItem(AltosUIPreferences.map_cache());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
map_cache.setSelectedItem (AltosUIPreferences.map_cache());
|
||||
pane.add(map_cache, constraints(1, 2, GridBagConstraints.BOTH));
|
||||
row++;
|
||||
}
|
||||
|
||||
public void add_bluetooth() {
|
||||
}
|
||||
|
||||
public void add_frequencies() {
|
||||
}
|
||||
|
||||
public AltosUIConfigure(JFrame in_owner, String name, String label) {
|
||||
super(in_owner, name, false);
|
||||
|
||||
owner = in_owner;
|
||||
pane = getScrollablePane();
|
||||
pane.setLayout(new GridBagLayout());
|
||||
|
||||
row = 0;
|
||||
|
||||
/* Nice label at the top */
|
||||
pane.add(new JLabel (label),
|
||||
constraints(0, 3));
|
||||
row++;
|
||||
|
||||
pane.add(new JLabel (String.format("AltOS version %s",
|
||||
AltosVersion.version)),
|
||||
constraints(0, 3));
|
||||
row++;
|
||||
|
||||
add_voice();
|
||||
add_log_dir();
|
||||
add_callsign();
|
||||
add_units();
|
||||
add_serial_debug();
|
||||
add_font_size();
|
||||
add_look_and_feel();
|
||||
add_position();
|
||||
add_map_cache();
|
||||
if (AltosUILib.has_bluetooth)
|
||||
add_bluetooth();
|
||||
add_frequencies();
|
||||
|
||||
/* And a close button at the bottom */
|
||||
JButton close = new JButton("Close");
|
||||
close.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
setVisible(false);
|
||||
}
|
||||
});
|
||||
pane.add(close, constraints(0, 3));
|
||||
|
||||
pack();
|
||||
setLocationRelativeTo(owner);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
public AltosUIConfigure(JFrame in_owner) {
|
||||
this(in_owner, "Configure AltosUI", "Configure AltOS UI");
|
||||
}
|
||||
}
|
||||
26
altosuilib/AltosUIDataMissing.java
Normal file
26
altosuilib/AltosUIDataMissing.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
public class AltosUIDataMissing extends Exception {
|
||||
public int id;
|
||||
public AltosUIDataMissing(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
26
altosuilib/AltosUIDataPoint.java
Normal file
26
altosuilib/AltosUIDataPoint.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
public interface AltosUIDataPoint {
|
||||
public abstract double x() throws AltosUIDataMissing;
|
||||
public abstract double y(int index) throws AltosUIDataMissing;
|
||||
public abstract int id(int index) throws AltosUIDataMissing;
|
||||
public abstract String id_name(int index) throws AltosUIDataMissing;
|
||||
}
|
||||
24
altosuilib/AltosUIDataSet.java
Normal file
24
altosuilib/AltosUIDataSet.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
public interface AltosUIDataSet {
|
||||
public abstract String name();
|
||||
public abstract Iterable<AltosUIDataPoint> dataPoints();
|
||||
}
|
||||
84
altosuilib/AltosUIDialog.java
Normal file
84
altosuilib/AltosUIDialog.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
|
||||
class AltosUIDialogListener extends WindowAdapter {
|
||||
public void windowClosing (WindowEvent e) {
|
||||
AltosUIPreferences.unregister_ui_listener((AltosUIDialog) e.getWindow());
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosUIDialog extends JDialog implements AltosUIListener {
|
||||
|
||||
private Container scrollPane;
|
||||
|
||||
public Container getScrollablePane() {
|
||||
if (scrollPane == null) {
|
||||
Container content = super.getContentPane();
|
||||
/* Create a container to hold the dialog contents */
|
||||
scrollPane = new Container();
|
||||
|
||||
/* Make an opaque box to use the right color */
|
||||
Box box = new Box(BoxLayout.X_AXIS);
|
||||
box.add(scrollPane);
|
||||
box.setOpaque(true);
|
||||
|
||||
/* Create a scrollpane to hold the box */
|
||||
JScrollPane scroll = new JScrollPane();
|
||||
JViewport view = scroll.getViewport();
|
||||
view.add(box);
|
||||
|
||||
/* Add the scroll pane to the top level */
|
||||
content.add(scroll);
|
||||
}
|
||||
return (Container) scrollPane;
|
||||
}
|
||||
|
||||
public void ui_changed(String look_and_feel) {
|
||||
SwingUtilities.updateComponentTreeUI(this);
|
||||
this.pack();
|
||||
}
|
||||
|
||||
public AltosUIDialog() {
|
||||
AltosUIPreferences.register_ui_listener(this);
|
||||
addWindowListener(new AltosUIDialogListener());
|
||||
}
|
||||
|
||||
public AltosUIDialog(Frame frame, String label, boolean modal) {
|
||||
super(frame, label, modal);
|
||||
AltosUIPreferences.register_ui_listener(this);
|
||||
addWindowListener(new AltosUIDialogListener());
|
||||
}
|
||||
|
||||
public AltosUIDialog(Dialog dialog, String label, boolean modal) {
|
||||
super(dialog, label, modal);
|
||||
AltosUIPreferences.register_ui_listener(this);
|
||||
addWindowListener(new AltosUIDialogListener());
|
||||
}
|
||||
|
||||
public AltosUIDialog(Frame frame, boolean modal) {
|
||||
super(frame, modal);
|
||||
AltosUIPreferences.register_ui_listener(this);
|
||||
addWindowListener(new AltosUIDialogListener());
|
||||
}
|
||||
}
|
||||
262
altosuilib/AltosUIEnable.java
Normal file
262
altosuilib/AltosUIEnable.java
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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.*;
|
||||
|
||||
public class AltosUIEnable extends Container implements ChangeListener {
|
||||
|
||||
Insets il, ir;
|
||||
int y;
|
||||
int x;
|
||||
JCheckBox imperial_units;
|
||||
JCheckBox show_shapes;
|
||||
JLabel line_width_label;
|
||||
JSpinner line_width;
|
||||
JLabel speed_filter_label;
|
||||
JSlider speed_filter;
|
||||
JLabel accel_filter_label;
|
||||
JSlider accel_filter;
|
||||
AltosFilterListener filter_listener;
|
||||
AltosShapeListener shape_listener;
|
||||
|
||||
static final int max_rows = 14;
|
||||
|
||||
public void units_changed(boolean imperial_units) {
|
||||
if (this.imperial_units != null) {
|
||||
this.imperial_units.setSelected(imperial_units);
|
||||
}
|
||||
}
|
||||
|
||||
class GraphElement implements ActionListener {
|
||||
AltosUIGrapher grapher;
|
||||
JCheckBox enable;
|
||||
String name;
|
||||
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
grapher.set_enable(enable.isSelected());
|
||||
}
|
||||
|
||||
GraphElement (String name, AltosUIGrapher grapher, boolean enabled) {
|
||||
this.name = name;
|
||||
this.grapher = grapher;
|
||||
enable = new JCheckBox(name, enabled);
|
||||
grapher.set_enable(enabled);
|
||||
enable.addActionListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
LinkedList<GraphElement> elements = new LinkedList<GraphElement>();
|
||||
|
||||
public void add(String name, AltosUIGrapher grapher, boolean enabled) {
|
||||
|
||||
GraphElement e = new GraphElement(name, grapher, enabled);
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
elements.add(e);
|
||||
|
||||
/* Add element */
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = x; c.gridy = y;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
c.insets = ir;
|
||||
add(e.enable, c);
|
||||
|
||||
/* Next row */
|
||||
y++;
|
||||
if (y == max_rows) {
|
||||
x++;
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
JSlider filter = (JSlider) e.getSource();
|
||||
if (!filter.getValueIsAdjusting()) {
|
||||
double speed_value = (int) speed_filter.getValue() / 1000.0;
|
||||
double accel_value = (int) accel_filter.getValue() / 1000.0;
|
||||
if (filter_listener != null) {
|
||||
filter_listener.filter_changed(speed_value, accel_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void set_shapes_visible(boolean visible) {
|
||||
if (shape_listener != null)
|
||||
shape_listener.set_shapes_visible(visible);
|
||||
}
|
||||
|
||||
public void set_line_width(float width) {
|
||||
if (shape_listener != null)
|
||||
shape_listener.set_line_width(width);
|
||||
}
|
||||
|
||||
public void register_shape_listener(AltosShapeListener shape_listener) {
|
||||
this.shape_listener = shape_listener;
|
||||
}
|
||||
|
||||
public void add_units() {
|
||||
/* Imperial units setting */
|
||||
|
||||
/* Add label */
|
||||
imperial_units = new JCheckBox("Imperial Units", AltosUIPreferences.imperial_units());
|
||||
imperial_units.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JCheckBox item = (JCheckBox) e.getSource();
|
||||
boolean enabled = item.isSelected();
|
||||
AltosUIPreferences.set_imperial_units(enabled);
|
||||
}
|
||||
});
|
||||
imperial_units.setToolTipText("Use Imperial units instead of metric");
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.gridx = 0; c.gridy = 1000;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = il;
|
||||
add(imperial_units, c);
|
||||
|
||||
show_shapes = new JCheckBox("Show Markers", false);
|
||||
show_shapes.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JCheckBox item = (JCheckBox) e.getSource();
|
||||
boolean enabled = item.isSelected();
|
||||
set_shapes_visible(enabled);
|
||||
}
|
||||
});
|
||||
show_shapes.setToolTipText("Show marker Use Imperial units instead of metric");
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 0; c.gridy = 1001;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = il;
|
||||
add(show_shapes, c);
|
||||
|
||||
|
||||
line_width_label = new JLabel("Line Width");
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 1; c.gridy = 1001;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = il;
|
||||
add(line_width_label, c);
|
||||
|
||||
line_width = new JSpinner();
|
||||
line_width.setValue(1);
|
||||
line_width.addChangeListener(new ChangeListener() {
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
int w = (Integer) line_width.getValue();
|
||||
if (w < 1) {
|
||||
w = 1;
|
||||
line_width.setValue(w);
|
||||
}
|
||||
set_line_width(w);
|
||||
}
|
||||
});
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 2; c.gridy = 1001;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = il;
|
||||
add(line_width, c);
|
||||
|
||||
speed_filter_label = new JLabel("Speed Filter(ms)");
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 0; c.gridy = 1002;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = il;
|
||||
add(speed_filter_label, c);
|
||||
|
||||
speed_filter = new JSlider(JSlider.HORIZONTAL, 0, 10000, (int) (filter_listener.speed_filter() * 1000.0));
|
||||
Hashtable<Integer,JLabel> label_table = new Hashtable<Integer,JLabel>();
|
||||
for (int i = 0; i <= 10000; i += 5000) {
|
||||
label_table.put(i, new JLabel(String.format("%d", i)));
|
||||
}
|
||||
speed_filter.setPaintTicks(true);
|
||||
speed_filter.setMajorTickSpacing(1000);
|
||||
speed_filter.setMinorTickSpacing(250);
|
||||
speed_filter.setLabelTable(label_table);
|
||||
speed_filter.setPaintTrack(false);
|
||||
speed_filter.setSnapToTicks(true);
|
||||
speed_filter.setPaintLabels(true);
|
||||
speed_filter.addChangeListener(this);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 1; c.gridy = 1002;
|
||||
c.gridwidth = 1000; c.gridheight = 1;
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = il;
|
||||
add(speed_filter, c);
|
||||
|
||||
accel_filter_label = new JLabel("Acceleration Filter(ms)");
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 0; c.gridy = 1003;
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = il;
|
||||
add(accel_filter_label, c);
|
||||
|
||||
accel_filter = new JSlider(JSlider.HORIZONTAL, 0, 10000, (int) (filter_listener.accel_filter() * 1000.0));
|
||||
accel_filter.setPaintTicks(true);
|
||||
accel_filter.setMajorTickSpacing(1000);
|
||||
accel_filter.setMinorTickSpacing(250);
|
||||
accel_filter.setLabelTable(label_table);
|
||||
accel_filter.setPaintTrack(false);
|
||||
accel_filter.setSnapToTicks(true);
|
||||
accel_filter.setPaintLabels(true);
|
||||
accel_filter.addChangeListener(this);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.gridx = 1; c.gridy = 1003;
|
||||
c.gridwidth = 1000; c.gridheight = 1;
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
c.anchor = GridBagConstraints.LINE_START;
|
||||
c.insets = il;
|
||||
add(accel_filter, c);
|
||||
}
|
||||
|
||||
public AltosUIEnable(AltosFilterListener filter_listener) {
|
||||
this.filter_listener = filter_listener;
|
||||
il = new Insets(4,4,4,4);
|
||||
ir = new Insets(4,4,4,4);
|
||||
x = 0;
|
||||
y = 0;
|
||||
setLayout(new GridBagLayout());
|
||||
add_units();
|
||||
}
|
||||
}
|
||||
134
altosuilib/AltosUIFlightSeries.java
Normal file
134
altosuilib/AltosUIFlightSeries.java
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright © 2017 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, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.util.*;
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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 AltosUITimeSeriesAxis {
|
||||
AltosUILineStyle line_style;
|
||||
boolean enabled;
|
||||
boolean marker;
|
||||
boolean marker_top;
|
||||
AltosUIAxis axis;
|
||||
XYPlot plot;
|
||||
|
||||
public AltosUITimeSeriesAxis(AltosUILineStyle line_style, boolean enabled,
|
||||
AltosUIAxis axis, XYPlot plot, boolean marker, boolean marker_top) {
|
||||
this.line_style = line_style;
|
||||
this.enabled = enabled;
|
||||
this.axis = axis;
|
||||
this.plot = plot;
|
||||
this.marker = marker;
|
||||
this.marker_top = marker_top;
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosUIFlightSeries extends AltosFlightSeries {
|
||||
|
||||
Hashtable<String,AltosUITimeSeriesAxis> axes;
|
||||
|
||||
void fill_axes(String label, AltosUITimeSeriesAxis axis) {
|
||||
for (AltosTimeSeries ts : series) {
|
||||
AltosUITimeSeries uts = (AltosUITimeSeries) ts;
|
||||
|
||||
if (label.equals(ts.label) || (label.equals("default") && uts.line_style == null)) {
|
||||
uts.custom_axis_set = true;
|
||||
if (axis.marker)
|
||||
uts.set_marker(axis.line_style, axis.enabled, axis.plot, axis.marker_top);
|
||||
else
|
||||
uts.set_axis(axis.line_style, axis.enabled, axis.axis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_axes() {
|
||||
for (AltosTimeSeries ts : series) {
|
||||
AltosUITimeSeries uts = (AltosUITimeSeries) ts;
|
||||
|
||||
if (!uts.custom_axis_set)
|
||||
System.out.printf("%s using default axis\n", ts.label);
|
||||
}
|
||||
}
|
||||
|
||||
public void register_axis(String label,
|
||||
AltosUILineStyle line_style,
|
||||
boolean enabled,
|
||||
AltosUIAxis axis) {
|
||||
AltosUITimeSeriesAxis tsa = new AltosUITimeSeriesAxis(line_style,
|
||||
enabled,
|
||||
axis,
|
||||
null,
|
||||
false,
|
||||
false);
|
||||
axes.put(label, tsa);
|
||||
fill_axes(label, tsa);
|
||||
}
|
||||
|
||||
public void register_marker(String label,
|
||||
AltosUILineStyle line_style,
|
||||
boolean enabled,
|
||||
XYPlot plot,
|
||||
boolean marker_top) {
|
||||
AltosUITimeSeriesAxis tsa = new AltosUITimeSeriesAxis(line_style,
|
||||
enabled,
|
||||
null,
|
||||
plot,
|
||||
true,
|
||||
marker_top);
|
||||
axes.put(label, tsa);
|
||||
fill_axes(label, tsa);
|
||||
}
|
||||
|
||||
public AltosTimeSeries make_series(String label, AltosUnits units) {
|
||||
|
||||
AltosUITimeSeries time_series = new AltosUITimeSeries(label, units);
|
||||
|
||||
AltosUITimeSeriesAxis tsa = axes.get(label);
|
||||
if (tsa == null)
|
||||
tsa = axes.get("default");
|
||||
else
|
||||
time_series.custom_axis_set = true;
|
||||
if (tsa != null) {
|
||||
if (tsa.marker)
|
||||
time_series.set_marker(tsa.line_style, tsa.enabled, tsa.plot, tsa.marker_top);
|
||||
else
|
||||
time_series.set_axis(tsa.line_style, tsa.enabled, tsa.axis);
|
||||
}
|
||||
return time_series;
|
||||
}
|
||||
|
||||
public AltosUITimeSeries[] series(AltosCalData cal_data) {
|
||||
finish();
|
||||
return series.toArray(new AltosUITimeSeries[0]);
|
||||
}
|
||||
|
||||
public AltosUIFlightSeries (AltosCalData cal_data) {
|
||||
super(cal_data);
|
||||
axes = new Hashtable<String,AltosUITimeSeriesAxis>();
|
||||
}
|
||||
}
|
||||
89
altosuilib/AltosUIFlightTab.java
Normal file
89
altosuilib/AltosUIFlightTab.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.util.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
336
altosuilib/AltosUIFrame.java
Normal file
336
altosuilib/AltosUIFrame.java
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
|
||||
class AltosUIFrameListener extends WindowAdapter {
|
||||
@Override
|
||||
public void windowClosing (WindowEvent e) {
|
||||
AltosUIFrame frame = (AltosUIFrame) e.getWindow();
|
||||
AltosUIPreferences.unregister_ui_listener(frame);
|
||||
AltosUIFrame.frame_closed();
|
||||
frame.setVisible(false);
|
||||
frame.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosUIFrame extends JFrame implements AltosUIListener, AltosPositionListener {
|
||||
|
||||
public void ui_changed(String look_and_feel) {
|
||||
SwingUtilities.updateComponentTreeUI(this);
|
||||
this.pack();
|
||||
}
|
||||
|
||||
private Container scrollPane;
|
||||
|
||||
public Container getScrollablePane() {
|
||||
if (scrollPane == null) {
|
||||
Container content = super.getContentPane();
|
||||
/* Create a container to hold the dialog contents */
|
||||
scrollPane = new Container();
|
||||
|
||||
/* Make an opaque box to use the right color */
|
||||
Box box = new Box(BoxLayout.X_AXIS);
|
||||
box.add(scrollPane);
|
||||
box.setOpaque(true);
|
||||
|
||||
/* Create a scrollpane to hold the box */
|
||||
JScrollPane scroll = new JScrollPane();
|
||||
JViewport view = scroll.getViewport();
|
||||
view.add(box);
|
||||
|
||||
/* Add the scroll pane to the top level */
|
||||
content.add(scroll);
|
||||
}
|
||||
return (Container) scrollPane;
|
||||
}
|
||||
|
||||
static String[] altos_icon_names = {
|
||||
"/altusmetrum-altosui-16.png",
|
||||
"/altusmetrum-altosui-32.png",
|
||||
"/altusmetrum-altosui-48.png",
|
||||
"/altusmetrum-altosui-64.png",
|
||||
"/altusmetrum-altosui-128.png",
|
||||
"/altusmetrum-altosui-256.png"
|
||||
};
|
||||
|
||||
static public String[] icon_names;
|
||||
|
||||
static public void set_icon_names(String[] new_icon_names) { icon_names = new_icon_names; }
|
||||
|
||||
public String[] icon_names() {
|
||||
if (icon_names == null)
|
||||
set_icon_names(altos_icon_names);
|
||||
return icon_names;
|
||||
}
|
||||
|
||||
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)
|
||||
icons.add(new ImageIcon(imgURL).getImage());
|
||||
}
|
||||
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();
|
||||
Dimension ps = rootPane.getPreferredSize();
|
||||
ps.width += i.left + i.right;
|
||||
ps.height += i.top + i.bottom;
|
||||
setPreferredSize(ps);
|
||||
setSize(ps);
|
||||
}
|
||||
|
||||
public void setPosition (int position) {
|
||||
Insets i = getInsets();
|
||||
Dimension ps = getSize();
|
||||
|
||||
/* Stick the window in the desired location on the screen */
|
||||
setLocationByPlatform(false);
|
||||
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
|
||||
GraphicsConfiguration gc = gd.getDefaultConfiguration();
|
||||
Rectangle r = gc.getBounds();
|
||||
|
||||
/* compute X position */
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
switch (position) {
|
||||
case AltosUILib.position_top_left:
|
||||
case AltosUILib.position_left:
|
||||
case AltosUILib.position_bottom_left:
|
||||
x = 0;
|
||||
break;
|
||||
case AltosUILib.position_top:
|
||||
case AltosUILib.position_center:
|
||||
case AltosUILib.position_bottom:
|
||||
x = (r.width - ps.width) / 2;
|
||||
break;
|
||||
case AltosUILib.position_top_right:
|
||||
case AltosUILib.position_right:
|
||||
case AltosUILib.position_bottom_right:
|
||||
x = r.width - ps.width + i.right;
|
||||
break;
|
||||
}
|
||||
|
||||
/* compute Y position */
|
||||
switch (position) {
|
||||
case AltosUILib.position_top_left:
|
||||
case AltosUILib.position_top:
|
||||
case AltosUILib.position_top_right:
|
||||
y = 0;
|
||||
break;
|
||||
case AltosUILib.position_left:
|
||||
case AltosUILib.position_center:
|
||||
case AltosUILib.position_right:
|
||||
y = (r.height - ps.height) / 2;
|
||||
break;
|
||||
case AltosUILib.position_bottom_left:
|
||||
case AltosUILib.position_bottom:
|
||||
case AltosUILib.position_bottom_right:
|
||||
y = r.height - ps.height + i.bottom;
|
||||
break;
|
||||
}
|
||||
setLocation(x, y);
|
||||
}
|
||||
|
||||
int position;
|
||||
|
||||
public void position_changed(int position) {
|
||||
this.position = position;
|
||||
if (!location_by_platform)
|
||||
setPosition(position);
|
||||
}
|
||||
|
||||
public void setVisible (boolean visible) {
|
||||
if (visible)
|
||||
setLocationByPlatform(location_by_platform);
|
||||
super.setVisible(visible);
|
||||
if (visible) {
|
||||
setSize();
|
||||
if (!location_by_platform)
|
||||
setPosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean global_settings_done;
|
||||
|
||||
public String getName() {
|
||||
return "Altus Metrum";
|
||||
}
|
||||
|
||||
public void macosx_quit_handler() {
|
||||
System.out.printf("Got quit handler\n");
|
||||
}
|
||||
|
||||
public void macosx_about_handler() {
|
||||
System.out.printf("Got about handler\n");
|
||||
}
|
||||
|
||||
public void macosx_preferences_handler() {
|
||||
System.out.printf("Got preferences handler\n");
|
||||
}
|
||||
|
||||
public void macosx_file_handler(String path) {
|
||||
System.out.printf("Got file handler with \"%s\"\n", path);
|
||||
}
|
||||
|
||||
/* Check that we are on Mac OS X. This is crucial to loading and using the OSXAdapter class.
|
||||
*/
|
||||
public static boolean MAC_OS_X = (System.getProperty("os.name").toLowerCase().startsWith("mac os x"));
|
||||
|
||||
private static boolean registered_for_macosx_events;
|
||||
|
||||
/* Generic registration with the Mac OS X application menu
|
||||
* Checks the platform, then attempts to register with the Apple EAWT
|
||||
* See OSXAdapter.java to see how this is done without directly referencing any Apple APIs
|
||||
*/
|
||||
public synchronized void register_for_macosx_events() {
|
||||
if (registered_for_macosx_events)
|
||||
return;
|
||||
registered_for_macosx_events = true;
|
||||
if (MAC_OS_X) {
|
||||
try {
|
||||
// Generate and register the OSXAdapter, passing it a hash of all the methods we wish to
|
||||
// use as delegates for various com.apple.eawt.ApplicationListener methods
|
||||
OSXAdapter.setQuitHandler(this, getClass().getDeclaredMethod("macosx_quit_handler", (Class[])null));
|
||||
// OSXAdapter.setAboutHandler(this, getClass().getDeclaredMethod("macosx_about_handler", (Class[])null));
|
||||
OSXAdapter.setPreferencesHandler(this, getClass().getDeclaredMethod("macosx_preferences_handler", (Class[])null));
|
||||
OSXAdapter.setFileHandler(this, getClass().getDeclaredMethod("macosx_file_handler", new Class[] { String.class }));
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error while loading the OSXAdapter:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int row = 0;
|
||||
|
||||
public void next_row() {
|
||||
row++;
|
||||
}
|
||||
|
||||
int inset = 0;
|
||||
|
||||
public void set_inset(int i) {
|
||||
inset = i;
|
||||
}
|
||||
|
||||
public GridBagConstraints constraints (int x, int width, int fill, int anchor, double weightx, double weighty) {
|
||||
return new GridBagConstraints(x, /* x */
|
||||
row, /* y */
|
||||
width, /* width */
|
||||
1, /* height */
|
||||
weightx, /* weightx */
|
||||
weighty, /* weighty */
|
||||
anchor, /* anchor */
|
||||
fill, /* fill */
|
||||
new Insets(inset,inset,inset,inset), /* insets */
|
||||
0, /* ipadx */
|
||||
0); /* ipady */
|
||||
}
|
||||
|
||||
public GridBagConstraints constraints (int x, int width, int fill, int anchor) {
|
||||
double weightx = 0;
|
||||
double weighty = 0;
|
||||
|
||||
if (fill == GridBagConstraints.NONE) {
|
||||
weightx = 0;
|
||||
weighty = 0;
|
||||
} else if (fill == GridBagConstraints.HORIZONTAL) {
|
||||
weightx = 1;
|
||||
weighty = 0;
|
||||
} else if (fill == GridBagConstraints.VERTICAL) {
|
||||
weightx = 0;
|
||||
weighty = 1;
|
||||
} else if (fill == GridBagConstraints.BOTH) {
|
||||
weightx = 1;
|
||||
weighty = 1;
|
||||
}
|
||||
|
||||
return constraints (x, width, fill, anchor, weightx, weighty);
|
||||
}
|
||||
|
||||
public GridBagConstraints constraints (int x, int width, int fill) {
|
||||
return constraints (x, width, fill, GridBagConstraints.WEST);
|
||||
}
|
||||
|
||||
public GridBagConstraints constraints(int x, int width) {
|
||||
return constraints(x, width, GridBagConstraints.NONE);
|
||||
}
|
||||
|
||||
static int open_frames;
|
||||
|
||||
public static void frame_opened() {
|
||||
++open_frames;
|
||||
}
|
||||
|
||||
public static void frame_closed() {
|
||||
--open_frames;
|
||||
if (open_frames == 0)
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
void init() {
|
||||
AltosUIPreferences.register_ui_listener(this);
|
||||
AltosUIPreferences.register_position_listener(this);
|
||||
position = AltosUIPreferences.position();
|
||||
frame_opened();
|
||||
addWindowListener(new AltosUIFrameListener());
|
||||
|
||||
/* Try to make menus live in the menu bar like regular Mac apps */
|
||||
if (!global_settings_done) {
|
||||
try {
|
||||
global_settings_done = true;
|
||||
System.setProperty("com.apple.mrj.application.apple.menu.about.name", getName());
|
||||
System.setProperty("com.apple.macos.useScreenMenuBar", "true");
|
||||
System.setProperty("apple.laf.useScreenMenuBar", "true" ); // for older versions of Java
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
set_icon();
|
||||
}
|
||||
|
||||
public AltosUIFrame() {
|
||||
init();
|
||||
}
|
||||
|
||||
public AltosUIFrame(String name) {
|
||||
super(name);
|
||||
init();
|
||||
}
|
||||
}
|
||||
87
altosuilib/AltosUIFreqList.java
Normal file
87
altosuilib/AltosUIFreqList.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosUIFreqList 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());
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
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 AltosUIFreqList () {
|
||||
super(AltosUIPreferences.common_frequencies());
|
||||
setMaximumRowCount(getItemCount());
|
||||
setEditable(false);
|
||||
product = "Unknown";
|
||||
serial = 0;
|
||||
}
|
||||
|
||||
public AltosUIFreqList(double in_frequency) {
|
||||
this();
|
||||
set_frequency(in_frequency);
|
||||
}
|
||||
}
|
||||
174
altosuilib/AltosUIGraph.java
Normal file
174
altosuilib/AltosUIGraph.java
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright © 2012 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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.*;
|
||||
|
||||
public class AltosUIGraph implements AltosUnitsListener, AltosShapeListener {
|
||||
|
||||
XYPlot plot;
|
||||
JFreeChart chart;
|
||||
public ChartPanel panel;
|
||||
NumberAxis xAxis;
|
||||
AltosUIEnable enable;
|
||||
AltosUITimeSeries[] series;
|
||||
int axis_index;
|
||||
int series_index;
|
||||
Hashtable<Integer,Boolean> axes_added;
|
||||
|
||||
static final private Color gridline_color = new Color(0, 0, 0);
|
||||
static final private Color border_color = new Color(255, 255, 255);
|
||||
static final private Color background_color = new Color(255, 255, 255);
|
||||
|
||||
public JPanel panel() {
|
||||
return panel;
|
||||
}
|
||||
|
||||
public AltosUIAxis newAxis(String label, AltosUnits units, AltosUILineStyle line_style, int flags) {
|
||||
AltosUIAxis axis = new AltosUIAxis(label, units, line_style, axis_index++, flags);
|
||||
plot.setRangeAxis(axis.index, axis);
|
||||
return axis;
|
||||
}
|
||||
|
||||
public AltosUIAxis newAxis(String label, AltosUnits units, AltosUILineStyle line_style) {
|
||||
return newAxis(label, units, line_style, AltosUIAxis.axis_default);
|
||||
}
|
||||
|
||||
void addAxis(AltosUIAxis axis) {
|
||||
if (!axes_added.containsKey(axis.index)) {
|
||||
axes_added.put(axis.index, true);
|
||||
plot.setRangeAxis(axis.index, axis);
|
||||
}
|
||||
}
|
||||
|
||||
public void addSeries(AltosUITimeSeries series) {
|
||||
XYSeriesCollection dataset = new XYSeriesCollection(series.xy_series());
|
||||
|
||||
addAxis(series.axis);
|
||||
|
||||
series.renderer.setPlot(plot);
|
||||
plot.setDataset(series_index, dataset);
|
||||
plot.setRenderer(series_index, series.renderer);
|
||||
plot.mapDatasetToRangeAxis(series_index, series.axis.index);
|
||||
if (enable != null)
|
||||
enable.add(series.label, series, series.enable);
|
||||
series_index++;
|
||||
}
|
||||
|
||||
public void addMarker(AltosUITimeSeries series) {
|
||||
}
|
||||
|
||||
public void units_changed(boolean imperial_units) {
|
||||
for (AltosUITimeSeries s : series)
|
||||
s.set_units();
|
||||
}
|
||||
|
||||
public void filter_changed() {
|
||||
units_changed(false);
|
||||
}
|
||||
|
||||
public void set_shapes_visible(boolean visible) {
|
||||
for (AltosUITimeSeries s : series)
|
||||
s.set_shapes_visible(visible);
|
||||
}
|
||||
|
||||
public void set_line_width(float width) {
|
||||
for (AltosUITimeSeries s : series)
|
||||
s.set_line_width(width);
|
||||
}
|
||||
|
||||
public void setName (String name) {
|
||||
chart.setTitle(name);
|
||||
}
|
||||
|
||||
public void set_series(AltosUITimeSeries[] series) {
|
||||
this.series = series;
|
||||
boolean any_enabled = false;
|
||||
|
||||
for (AltosUITimeSeries s : series)
|
||||
if (s.enable)
|
||||
any_enabled = true;
|
||||
|
||||
if (!any_enabled)
|
||||
for (AltosUITimeSeries s : series)
|
||||
s.set_enable(true);
|
||||
|
||||
for (AltosUITimeSeries s : series)
|
||||
addSeries(s);
|
||||
|
||||
units_changed(false);
|
||||
}
|
||||
|
||||
public AltosUIGraph(AltosUIEnable enable, String title) {
|
||||
|
||||
this.enable = enable;
|
||||
this.series = null;
|
||||
this.axis_index = 0;
|
||||
|
||||
if (enable != null)
|
||||
enable.register_shape_listener(this);
|
||||
|
||||
axes_added = new Hashtable<Integer,Boolean>();
|
||||
|
||||
xAxis = new NumberAxis("Time (s)");
|
||||
|
||||
xAxis.setAutoRangeIncludesZero(true);
|
||||
|
||||
plot = new XYPlot();
|
||||
plot.setDomainAxis(xAxis);
|
||||
plot.setOrientation(PlotOrientation.VERTICAL);
|
||||
plot.setDomainPannable(true);
|
||||
plot.setRangePannable(true);
|
||||
|
||||
chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT,
|
||||
plot, true);
|
||||
|
||||
ChartUtilities.applyCurrentTheme(chart);
|
||||
|
||||
plot.setDomainGridlinePaint(gridline_color);
|
||||
plot.setRangeGridlinePaint(gridline_color);
|
||||
plot.setBackgroundPaint(background_color);
|
||||
plot.setBackgroundAlpha((float) 1);
|
||||
|
||||
chart.setBackgroundPaint(background_color);
|
||||
chart.setBorderPaint(border_color);
|
||||
panel = new ChartPanel(chart);
|
||||
panel.setMouseWheelEnabled(true);
|
||||
panel.setPreferredSize(new java.awt.Dimension(800, 500));
|
||||
|
||||
AltosPreferences.register_units_listener(this);
|
||||
|
||||
}
|
||||
}
|
||||
53
altosuilib/AltosUIGrapher.java
Normal file
53
altosuilib/AltosUIGrapher.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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.*;
|
||||
|
||||
interface AltosUIGrapher {
|
||||
|
||||
public abstract void set_units();
|
||||
|
||||
public abstract boolean need_reset();
|
||||
|
||||
public abstract void clear();
|
||||
|
||||
public abstract void add(AltosUIDataPoint dataPoint);
|
||||
|
||||
public abstract void set_enable(boolean enable);
|
||||
|
||||
public abstract void setNotify(boolean notify);
|
||||
|
||||
public abstract void fireSeriesChanged();
|
||||
}
|
||||
39
altosuilib/AltosUIImage.java
Normal file
39
altosuilib/AltosUIImage.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright © 2015 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altoslib_14;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.*;
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
public class AltosUIImage implements AltosImage {
|
||||
public Image image;
|
||||
|
||||
/* Discard storage for image */
|
||||
public void flush() {
|
||||
image.flush();
|
||||
}
|
||||
|
||||
public AltosUIImage(Image image) {
|
||||
this.image = image;
|
||||
}
|
||||
}
|
||||
182
altosuilib/AltosUIIndicator.java
Normal file
182
altosuilib/AltosUIIndicator.java
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
127
altosuilib/AltosUILib.java
Normal file
127
altosuilib/AltosUILib.java
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import libaltosJNI.*;
|
||||
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosUILib extends AltosLib {
|
||||
|
||||
public static final int tab_elt_pad = 5;
|
||||
|
||||
public static Font label_font;
|
||||
public static Font value_font;
|
||||
public static Font status_font;
|
||||
public static Font table_label_font;
|
||||
public static Font table_value_font;
|
||||
|
||||
final public static int font_size_small = 1;
|
||||
final public static int font_size_medium = 2;
|
||||
final public static int font_size_large = 3;
|
||||
final public static int font_size_huge = 4;
|
||||
|
||||
final public static int position_top_left = 0;
|
||||
final public static int position_top = 1;
|
||||
final public static int position_top_right = 2;
|
||||
final public static int position_left = 3;
|
||||
final public static int position_center = 4;
|
||||
final public static int position_right = 5;
|
||||
final public static int position_bottom_left = 6;
|
||||
final public static int position_bottom = 7;
|
||||
final public static int position_bottom_right = 8;
|
||||
|
||||
public static void set_fonts(int size) {
|
||||
int brief_size;
|
||||
int table_size;
|
||||
int status_size;
|
||||
|
||||
switch (size) {
|
||||
case font_size_small:
|
||||
brief_size = 16;
|
||||
status_size = 18;
|
||||
table_size = 11;
|
||||
break;
|
||||
default:
|
||||
case font_size_medium:
|
||||
brief_size = 22;
|
||||
status_size = 24;
|
||||
table_size = 14;
|
||||
break;
|
||||
case font_size_large:
|
||||
brief_size = 26;
|
||||
status_size = 30;
|
||||
table_size = 17;
|
||||
break;
|
||||
case font_size_huge:
|
||||
brief_size = 30;
|
||||
status_size = 36;
|
||||
table_size = 24;
|
||||
break;
|
||||
}
|
||||
label_font = new Font("Dialog", Font.PLAIN, brief_size);
|
||||
value_font = new Font("Monospaced", Font.PLAIN, brief_size);
|
||||
status_font = new Font("SansSerif", Font.BOLD, status_size);
|
||||
table_label_font = new Font("SansSerif", Font.PLAIN, table_size);
|
||||
table_value_font = new Font("Monospaced", Font.PLAIN, table_size);
|
||||
}
|
||||
|
||||
static public final int text_width = 20;
|
||||
|
||||
static public boolean initialized = false;
|
||||
static public boolean loaded_library = false;
|
||||
static public boolean has_bluetooth = false;
|
||||
|
||||
static final String[] library_names = {
|
||||
"altos",
|
||||
"altos32",
|
||||
"altos64",
|
||||
"altos_i686",
|
||||
"altos_amd64",
|
||||
"altos_aarch64",
|
||||
"altos_armel",
|
||||
"altos_armhf"
|
||||
};
|
||||
|
||||
public static boolean load_library() {
|
||||
if (!initialized) {
|
||||
for (String name : library_names) {
|
||||
try {
|
||||
System.loadLibrary(name);
|
||||
libaltos.altos_init();
|
||||
loaded_library = true;
|
||||
break;
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
loaded_library = false;
|
||||
}
|
||||
}
|
||||
if (!loaded_library)
|
||||
System.out.printf("Cannot find 'libaltos' device access library\n");
|
||||
|
||||
String OS = System.getProperty("os.name");
|
||||
|
||||
if (OS.startsWith("L") || OS.startsWith("W"))
|
||||
has_bluetooth = true;
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
return loaded_library;
|
||||
}
|
||||
}
|
||||
84
altosuilib/AltosUILineStyle.java
Normal file
84
altosuilib/AltosUILineStyle.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright © 2017 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, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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.*;
|
||||
|
||||
public class AltosUILineStyle {
|
||||
public Color color;
|
||||
public float[] dash;
|
||||
|
||||
static private Color color(int r, int g, int b) {
|
||||
return new Color(r,g,b);
|
||||
}
|
||||
|
||||
static final private Color[] colors = {
|
||||
new Color(0,0,0),
|
||||
new Color(230,0,0), // red
|
||||
new Color(216,103,0), // orange
|
||||
new Color(200,200,0), // yellow
|
||||
new Color(0,180,0), // green
|
||||
new Color(0,140,140), // cyan
|
||||
new Color(130,0,0), // dark red
|
||||
new Color(0,100,0), // dark green
|
||||
new Color(0,0,255), // blue
|
||||
new Color(140,0,140), // magenta
|
||||
new Color(150,150,150), // gray
|
||||
};
|
||||
|
||||
static final private float[][] dashes = {
|
||||
{ 0 },
|
||||
{ 2, 4 },
|
||||
{ 4, 4 },
|
||||
{ 6, 4 },
|
||||
{ 6, 4, 2, 4 }
|
||||
};
|
||||
|
||||
static int color_index, dash_index;
|
||||
|
||||
public AltosUILineStyle () {
|
||||
color = colors[color_index];
|
||||
dash = dashes[dash_index];
|
||||
color_index = (color_index + 1) % colors.length;
|
||||
if (color_index == 0) {
|
||||
dash_index = (dash_index + 1) % dashes.length;
|
||||
if (dash_index == 0)
|
||||
System.out.printf("too many line styles\n");
|
||||
}
|
||||
}
|
||||
|
||||
public AltosUILineStyle(int index) {
|
||||
index = index % (colors.length * dashes.length);
|
||||
int c = index % colors.length;
|
||||
int d = index / colors.length;
|
||||
color = colors[c];
|
||||
dash = dashes[d];
|
||||
}
|
||||
}
|
||||
23
altosuilib/AltosUIListener.java
Normal file
23
altosuilib/AltosUIListener.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
public interface AltosUIListener {
|
||||
public void ui_changed(String look_and_feel);
|
||||
}
|
||||
675
altosuilib/AltosUIMap.java
Normal file
675
altosuilib/AltosUIMap.java
Normal file
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.image.*;
|
||||
import javax.swing.*;
|
||||
import java.io.*;
|
||||
import java.lang.Math;
|
||||
import java.awt.geom.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import javax.imageio.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosMapInterface {
|
||||
|
||||
AltosMap map;
|
||||
Graphics2D g;
|
||||
Font tile_font;
|
||||
Font line_font;
|
||||
AltosMapMark nearest_mark;
|
||||
|
||||
static Point2D.Double point2d(AltosPointDouble pt) {
|
||||
return new Point2D.Double(pt.x, pt.y);
|
||||
}
|
||||
|
||||
static final AltosPointDouble point_double(Point pt) {
|
||||
return new AltosPointDouble(pt.x, pt.y);
|
||||
}
|
||||
|
||||
class MapMark extends AltosMapMark {
|
||||
public void paint(AltosMapTransform t) {
|
||||
double lat = lat_lon.lat;
|
||||
double lon;
|
||||
double first_lon = t.first_lon(lat_lon.lon);
|
||||
double last_lon = t.last_lon(lat_lon.lon);
|
||||
for (lon = first_lon; lon <= last_lon; lon += 360.0) {
|
||||
AltosPointDouble 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);
|
||||
|
||||
if (label != null) {
|
||||
Rectangle2D bounds;
|
||||
bounds = line_font.getStringBounds(label, g.getFontRenderContext());
|
||||
float x = (float) pt.x;
|
||||
float y = (float) pt.y + (float) bounds.getHeight() / 2.0f;
|
||||
|
||||
g.setFont(line_font);
|
||||
g.setColor(Color.WHITE);
|
||||
for (int dy = -2; dy <= 2; dy += 2)
|
||||
for (int dx = -2; dx <= 2; dx += 2)
|
||||
g.drawString(label, x + dx, y + dy);
|
||||
if (0 <= state && state < AltosUIMap.stateColors.length)
|
||||
g.setColor(AltosUIMap.stateColors[state]);
|
||||
else
|
||||
g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
|
||||
g.drawString(label, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapMark(double lat, double lon, int state, String label) {
|
||||
super(lat, lon, state, label);
|
||||
}
|
||||
|
||||
MapMark(double lat, double lon, int state) {
|
||||
super(lat, lon, state);
|
||||
}
|
||||
}
|
||||
|
||||
class MapView extends JComponent implements MouseMotionListener, MouseListener, ComponentListener, MouseWheelListener {
|
||||
|
||||
private VolatileImage create_back_buffer() {
|
||||
return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight());
|
||||
}
|
||||
|
||||
private void do_paint(Graphics my_g) {
|
||||
g = (Graphics2D) my_g;
|
||||
|
||||
map.paint();
|
||||
}
|
||||
|
||||
public void paint(Graphics my_g) {
|
||||
VolatileImage back_buffer = create_back_buffer();
|
||||
|
||||
Graphics2D top_g = (Graphics2D) my_g;
|
||||
|
||||
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(top_g.getClip());
|
||||
do_paint(g_back);
|
||||
g_back.dispose();
|
||||
|
||||
top_g.drawImage(back_buffer, 0, 0, this);
|
||||
} while (back_buffer.contentsLost());
|
||||
back_buffer.flush();
|
||||
}
|
||||
|
||||
public void repaint(AltosRectangle damage) {
|
||||
repaint(damage.x, damage.y, damage.width, damage.height);
|
||||
}
|
||||
|
||||
private boolean is_drag_event(MouseEvent e) {
|
||||
return e.getModifiersEx() == InputEvent.BUTTON1_DOWN_MASK;
|
||||
}
|
||||
|
||||
/* MouseMotionListener methods */
|
||||
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
map.touch_continue(e.getPoint().x, e.getPoint().y, is_drag_event(e));
|
||||
}
|
||||
|
||||
String pos(double p, String pos, String neg) {
|
||||
if (p == AltosLib.MISSING)
|
||||
return "";
|
||||
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);
|
||||
}
|
||||
|
||||
String height(double h, String label) {
|
||||
if (h == AltosLib.MISSING)
|
||||
return "";
|
||||
return String.format(" %s%s",
|
||||
AltosConvert.height.show(6, h),
|
||||
label);
|
||||
}
|
||||
|
||||
String speed(double s, String label) {
|
||||
if (s == AltosLib.MISSING)
|
||||
return "";
|
||||
return String.format(" %s%s",
|
||||
AltosConvert.speed.show(6, s),
|
||||
label);
|
||||
}
|
||||
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
AltosMapPathPoint point = map.nearest(e.getPoint().x, e.getPoint().y);
|
||||
|
||||
if (point != null) {
|
||||
if (nearest_mark == null)
|
||||
nearest_mark = map.add_mark(point.gps.lat,
|
||||
point.gps.lon,
|
||||
point.state);
|
||||
else {
|
||||
nearest_mark.lat_lon.lat = point.gps.lat;
|
||||
nearest_mark.lat_lon.lon = point.gps.lon;
|
||||
nearest_mark.state = point.state;
|
||||
}
|
||||
nearest_label.setText(String.format("%9.2f sec %s%s%s%s",
|
||||
point.time,
|
||||
pos(point.gps.lat,
|
||||
"N", "S"),
|
||||
pos(point.gps.lon,
|
||||
"E", "W"),
|
||||
height(point.gps_height, ""),
|
||||
speed(point.gps.ground_speed, "(h)"),
|
||||
speed(point.gps.climb_rate, "(v)")));
|
||||
} else {
|
||||
nearest_label.setText("");
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
/* MouseListener methods */
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e) {
|
||||
map.touch_start(e.getPoint().x, e.getPoint().y, is_drag_event(e));
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
}
|
||||
|
||||
/* MouseWheelListener methods */
|
||||
|
||||
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||
int zoom_change = e.getWheelRotation();
|
||||
|
||||
map.set_zoom_centre(map.get_zoom() - zoom_change, new AltosPointInt(e.getPoint().x, e.getPoint().y));
|
||||
}
|
||||
|
||||
/* ComponentListener methods */
|
||||
|
||||
public void componentHidden(ComponentEvent e) {
|
||||
}
|
||||
|
||||
public void componentMoved(ComponentEvent e) {
|
||||
}
|
||||
|
||||
public void componentResized(ComponentEvent e) {
|
||||
map.set_transform();
|
||||
}
|
||||
|
||||
public void componentShown(ComponentEvent e) {
|
||||
map.set_transform();
|
||||
}
|
||||
|
||||
MapView() {
|
||||
addComponentListener(this);
|
||||
addMouseMotionListener(this);
|
||||
addMouseListener(this);
|
||||
addMouseWheelListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
class MapLine extends AltosMapLine {
|
||||
|
||||
public void paint(AltosMapTransform t) {
|
||||
|
||||
if (start == null || end == null)
|
||||
return;
|
||||
|
||||
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
Line2D.Double line = new Line2D.Double(point2d(t.screen(start)),
|
||||
point2d(t.screen(end)));
|
||||
|
||||
g.setColor(Color.WHITE);
|
||||
g.setStroke(new BasicStroke(stroke_width+4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||||
g.draw(line);
|
||||
|
||||
g.setColor(Color.BLUE);
|
||||
g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||||
g.draw(line);
|
||||
|
||||
String message = line_dist();
|
||||
Rectangle2D bounds;
|
||||
bounds = line_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.setFont(line_font);
|
||||
g.setColor(Color.WHITE);
|
||||
for (int dy = -2; dy <= 2; dy += 2)
|
||||
for (int dx = -2; dx <= 2; dx += 2)
|
||||
g.drawString(message, x + dx, y + dy);
|
||||
g.setColor(Color.BLUE);
|
||||
g.drawString(message, x, y);
|
||||
}
|
||||
|
||||
public MapLine() {
|
||||
}
|
||||
}
|
||||
|
||||
class MapPath extends AltosMapPath {
|
||||
public void paint(AltosMapTransform 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 (AltosMapPathPoint point : points) {
|
||||
Point2D.Double cur = point2d(t.screen(point.gps.lat, point.gps.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MapTile extends AltosMapTile {
|
||||
public MapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||
super(cache, upper_left, center, zoom, maptype, px_size, scale);
|
||||
}
|
||||
|
||||
public void paint(AltosMapTransform t) {
|
||||
|
||||
AltosPointDouble point_double = t.screen(upper_left);
|
||||
Point point = new Point((int) (point_double.x + 0.5),
|
||||
(int) (point_double.y + 0.5));
|
||||
|
||||
if (!g.hitClip(point.x, point.y, px_size, px_size))
|
||||
return;
|
||||
|
||||
AltosImage altos_image = get_image();
|
||||
AltosUIImage ui_image = (AltosUIImage) altos_image;
|
||||
Image image = null;
|
||||
|
||||
if (ui_image != null)
|
||||
image = ui_image.image;
|
||||
|
||||
if (image != null) {
|
||||
g.drawImage(image, point.x, point.y, null);
|
||||
/*
|
||||
* Useful when debugging map fetching problems
|
||||
*
|
||||
String message = String.format("%.6f %.6f", center.lat, center.lon);
|
||||
g.setFont(tile_font);
|
||||
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
Rectangle2D bounds = tile_font.getStringBounds(message, g.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;
|
||||
g.setColor(Color.RED);
|
||||
g.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
|
||||
*/
|
||||
} else {
|
||||
g.setColor(Color.GRAY);
|
||||
g.fillRect(point.x, point.y, px_size, px_size);
|
||||
|
||||
if (t.has_location()) {
|
||||
String message = null;
|
||||
switch (status) {
|
||||
case AltosMapTile.fetching:
|
||||
message = "Fetching...";
|
||||
break;
|
||||
case AltosMapTile.bad_request:
|
||||
message = "Internal error";
|
||||
break;
|
||||
case AltosMapTile.failed:
|
||||
message = "Network error";
|
||||
break;
|
||||
case AltosMapTile.forbidden:
|
||||
message = "Outside of known launch areas";
|
||||
break;
|
||||
}
|
||||
if (message != null && tile_font != null) {
|
||||
g.setFont(tile_font);
|
||||
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
Rectangle2D bounds = tile_font.getStringBounds(message, g.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;
|
||||
g.setColor(Color.BLACK);
|
||||
g.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
/* AltosMapInterface functions */
|
||||
|
||||
public AltosMapPath new_path() {
|
||||
return new MapPath();
|
||||
}
|
||||
|
||||
public AltosMapLine new_line() {
|
||||
return new MapLine();
|
||||
}
|
||||
|
||||
public AltosImage load_image(File file) throws Exception {
|
||||
return new AltosUIImage(ImageIO.read(file));
|
||||
}
|
||||
|
||||
public AltosMapMark new_mark(double lat, double lon, int state) {
|
||||
return new MapMark(lat, lon, state);
|
||||
}
|
||||
|
||||
public AltosMapMark new_mark(double lat, double lon, int state, String label) {
|
||||
return new MapMark(lat, lon, state, label);
|
||||
}
|
||||
|
||||
public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||
return new MapTile(cache, upper_left, center, zoom, maptype, px_size, scale);
|
||||
}
|
||||
|
||||
public int width() {
|
||||
return view.getWidth();
|
||||
}
|
||||
|
||||
public int height() {
|
||||
return view.getHeight();
|
||||
}
|
||||
|
||||
public void repaint() {
|
||||
view.repaint();
|
||||
}
|
||||
|
||||
public void repaint(AltosRectangle damage) {
|
||||
view.repaint(damage);
|
||||
}
|
||||
|
||||
public void set_zoom_label(String label) {
|
||||
zoom_label.setText(label);
|
||||
}
|
||||
|
||||
public void select_object(AltosLatLon latlon) {
|
||||
debug("select at %f,%f\n", latlon.lat, latlon.lon);
|
||||
}
|
||||
|
||||
public void debug(String format, Object ... arguments) {
|
||||
if (AltosUIPreferences.serial_debug())
|
||||
System.out.printf(format, arguments);
|
||||
}
|
||||
|
||||
|
||||
/* AltosFlightDisplay interface */
|
||||
|
||||
public void set_font() {
|
||||
tile_font = AltosUILib.value_font;
|
||||
line_font = AltosUILib.status_font;
|
||||
if (nearest_label != null)
|
||||
nearest_label.setFont(AltosUILib.value_font);
|
||||
}
|
||||
|
||||
public void font_size_changed(int font_size) {
|
||||
set_font();
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void units_changed(boolean imperial_units) {
|
||||
repaint();
|
||||
}
|
||||
|
||||
JLabel zoom_label;
|
||||
|
||||
JLabel nearest_label;
|
||||
|
||||
public void set_maptype(int type) {
|
||||
/*
|
||||
map.set_maptype(type);
|
||||
maptype_combo.setSelectedIndex(type);
|
||||
*/
|
||||
}
|
||||
|
||||
/* AltosUIMapPreload functions */
|
||||
|
||||
public void set_zoom(int zoom) {
|
||||
map.set_zoom(zoom);
|
||||
}
|
||||
|
||||
public void add_mark(double lat, double lon, int status) {
|
||||
map.add_mark(lat, lon, status);
|
||||
}
|
||||
|
||||
public void add_mark(double lat, double lon, int status, String label) {
|
||||
map.add_mark(lat, lon, status, label);
|
||||
}
|
||||
|
||||
public void clear_marks() {
|
||||
map.clear_marks();
|
||||
}
|
||||
|
||||
/* AltosFlightDisplay interface */
|
||||
public void reset() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
public void show(AltosState state, AltosListenerState listener_state) {
|
||||
map.show(state, listener_state);
|
||||
}
|
||||
|
||||
public void show(AltosGPS gps, double time, int state, double gps_height) {
|
||||
map.show(gps, time, state, gps_height);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "Map";
|
||||
}
|
||||
|
||||
/* AltosGraphUI interface */
|
||||
public void centre(AltosState state) {
|
||||
map.centre(state);
|
||||
}
|
||||
|
||||
public void centre(AltosGPS gps) {
|
||||
map.centre(gps);
|
||||
}
|
||||
|
||||
/* internal layout bits */
|
||||
private GridBagLayout layout = new GridBagLayout();
|
||||
|
||||
/*
|
||||
JComboBox<String> maptype_combo;
|
||||
*/
|
||||
|
||||
MapView view;
|
||||
|
||||
public AltosUIMap() {
|
||||
|
||||
set_font();
|
||||
|
||||
view = new MapView();
|
||||
|
||||
view.setPreferredSize(new Dimension(500,500));
|
||||
view.setVisible(true);
|
||||
view.setEnabled(true);
|
||||
|
||||
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);
|
||||
|
||||
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) {
|
||||
map.set_zoom(map.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) {
|
||||
map.set_zoom(map.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) {
|
||||
map.set_zoom(map.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);
|
||||
|
||||
|
||||
nearest_label = new JLabel("", JLabel.LEFT);
|
||||
nearest_label.setFont(tile_font);
|
||||
|
||||
c = new GridBagConstraints();
|
||||
c.anchor = GridBagConstraints.CENTER;
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.gridx = 0;
|
||||
c.gridy = 11;
|
||||
c.weightx = 0;
|
||||
c.weighty = 0;
|
||||
c.gridwidth = 1;
|
||||
c.gridheight = 1;
|
||||
add(nearest_label, c);
|
||||
/*
|
||||
maptype_combo = new JComboBox<String>(map.maptype_labels);
|
||||
|
||||
maptype_combo.setEditable(false);
|
||||
maptype_combo.setMaximumRowCount(maptype_combo.getItemCount());
|
||||
maptype_combo.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
map.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);
|
||||
*/
|
||||
map = new AltosMap(this);
|
||||
}
|
||||
}
|
||||
576
altosuilib/AltosUIMapPreload.java
Normal file
576
altosuilib/AltosUIMapPreload.java
Normal file
@@ -0,0 +1,576 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
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_14.*;
|
||||
|
||||
class AltosUIMapPos extends Box implements ActionListener {
|
||||
AltosUIMapPreload preload;
|
||||
AltosUIFrame owner;
|
||||
JLabel label;
|
||||
JComboBox hemi;
|
||||
JTextField deg;
|
||||
JLabel deg_label;
|
||||
JTextField min;
|
||||
JLabel min_label;
|
||||
|
||||
/* ActionListener interface */
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
preload.center_map();
|
||||
}
|
||||
|
||||
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 ParseException {
|
||||
int h = hemi.getSelectedIndex();
|
||||
String d_t = deg.getText();
|
||||
String m_t = min.getText();
|
||||
double d, m, v;
|
||||
try {
|
||||
d = AltosParse.parse_double_locale(d_t);
|
||||
} catch (ParseException pe) {
|
||||
JOptionPane.showMessageDialog(owner,
|
||||
String.format("Invalid degrees \"%s\"",
|
||||
d_t),
|
||||
"Invalid number",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
throw pe;
|
||||
}
|
||||
try {
|
||||
if (m_t.equals(""))
|
||||
m = 0;
|
||||
else
|
||||
m = AltosParse.parse_double_locale(m_t);
|
||||
} catch (ParseException pe) {
|
||||
JOptionPane.showMessageDialog(owner,
|
||||
String.format("Invalid minutes \"%s\"",
|
||||
m_t),
|
||||
"Invalid number",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
throw pe;
|
||||
}
|
||||
v = d + m/60.0;
|
||||
if (h == 1)
|
||||
v = -v;
|
||||
return v;
|
||||
}
|
||||
|
||||
public AltosUIMapPos(AltosUIFrame in_owner,
|
||||
AltosUIMapPreload preload,
|
||||
String label_value,
|
||||
String[] hemi_names,
|
||||
double default_value) {
|
||||
super(BoxLayout.X_AXIS);
|
||||
owner = in_owner;
|
||||
this.preload = preload;
|
||||
label = new JLabel(label_value);
|
||||
hemi = new JComboBox<String>(hemi_names);
|
||||
hemi.setEditable(false);
|
||||
deg = new JTextField(5);
|
||||
deg.addActionListener(this);
|
||||
deg.setMinimumSize(deg.getPreferredSize());
|
||||
deg.setHorizontalAlignment(JTextField.RIGHT);
|
||||
deg_label = new JLabel("°");
|
||||
min = new JTextField(9);
|
||||
min.addActionListener(this);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, ItemListener, AltosLaunchSiteListener, AltosMapLoaderListener, AltosUnitsListener, AltosFontListener {
|
||||
AltosUIFrame owner;
|
||||
AltosUIMap map;
|
||||
|
||||
AltosUIMapPos lat;
|
||||
AltosUIMapPos lon;
|
||||
|
||||
JProgressBar pbar;
|
||||
|
||||
JLabel site_list_label;
|
||||
java.util.List<AltosLaunchSite> sites;
|
||||
JComboBox<AltosLaunchSite> site_list;
|
||||
|
||||
JToggleButton load_button;
|
||||
JButton close_button;
|
||||
|
||||
/*
|
||||
JCheckBox[] maptypes = new JCheckBox[AltosMap.maptype_terrain - AltosMap.maptype_hybrid + 1];
|
||||
*/
|
||||
|
||||
JComboBox<Integer> min_zoom;
|
||||
JComboBox<Integer> max_zoom;
|
||||
JLabel radius_label;
|
||||
JComboBox<Double> radius;
|
||||
int scale = 1;
|
||||
|
||||
Integer[] zooms = { -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 };
|
||||
|
||||
Double[] radius_mi = { 1.0, 2.0, 5.0, 10.0, 20.0 };
|
||||
Double radius_def_mi = 5.0;
|
||||
Double[] radius_km = { 2.0, 5.0, 10.0, 20.0, 30.0 };
|
||||
Double radius_def_km = 10.0;
|
||||
|
||||
AltosMapLoader loader;
|
||||
|
||||
static final String[] lat_hemi_names = { "N", "S" };
|
||||
static final String[] lon_hemi_names = { "E", "W" };
|
||||
|
||||
double latitude, longitude;
|
||||
|
||||
long loader_notify_time;
|
||||
|
||||
/* AltosMapLoaderListener interfaces */
|
||||
public void loader_start(final int max) {
|
||||
loader_notify_time = System.currentTimeMillis();
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
pbar.setMaximum(max);
|
||||
pbar.setValue(0);
|
||||
pbar.setString("");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void loader_notify(final int cur, final int max, final String name) {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (now - loader_notify_time < 100)
|
||||
return;
|
||||
|
||||
loader_notify_time = now;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
pbar.setValue(cur);
|
||||
pbar.setString(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void loader_done(int max) {
|
||||
loader = null;
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
pbar.setValue(0);
|
||||
pbar.setString("");
|
||||
load_button.setSelected(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void debug(String format, Object ... arguments) {
|
||||
if (AltosSerial.debug)
|
||||
System.out.printf(format, arguments);
|
||||
}
|
||||
|
||||
|
||||
private int all_types() {
|
||||
/*
|
||||
int all_types = 0;
|
||||
for (int t = AltosMap.maptype_hybrid; t <= AltosMap.maptype_terrain; t++)
|
||||
if (maptypes[t].isSelected())
|
||||
all_types |= (1 << t);
|
||||
return all_types;
|
||||
*/
|
||||
return 1 << AltosMap.maptype_hybrid;
|
||||
}
|
||||
|
||||
void add_mark(double lat, double lon, int state, String label) {
|
||||
map.add_mark(lat, lon, state, label);
|
||||
}
|
||||
|
||||
void reset_marks() {
|
||||
map.clear_marks();
|
||||
AltosLatLon centre = null;
|
||||
String centre_name = null;
|
||||
if (map != null && map.map != null)
|
||||
centre = map.map.centre;
|
||||
for (AltosLaunchSite site : sites) {
|
||||
if (centre != null && centre.lat == site.latitude && centre.lon == site.longitude)
|
||||
centre_name = site.name;
|
||||
else
|
||||
add_mark(site.latitude, site.longitude, AltosLib.ao_flight_main, site.name);
|
||||
}
|
||||
if (centre != null)
|
||||
add_mark(centre.lat, centre.lon, AltosLib.ao_flight_boost, centre_name);
|
||||
}
|
||||
|
||||
void center_map(double latitude, double longitude) {
|
||||
map.map.centre(new AltosLatLon(latitude, longitude));
|
||||
reset_marks();
|
||||
}
|
||||
|
||||
void center_map() {
|
||||
try {
|
||||
center_map(lat.get_value(), lon.get_value());
|
||||
} catch (ParseException pe) {
|
||||
}
|
||||
}
|
||||
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
int state = e.getStateChange();
|
||||
|
||||
if (state == ItemEvent.SELECTED) {
|
||||
Object o = e.getItem();
|
||||
if (o instanceof AltosLaunchSite) {
|
||||
AltosLaunchSite site = (AltosLaunchSite) o;
|
||||
lat.set_value(site.latitude);
|
||||
lon.set_value(site.longitude);
|
||||
center_map(site.latitude, site.longitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
String cmd = e.getActionCommand();
|
||||
|
||||
if (cmd.equals("close")) {
|
||||
if (loader != null)
|
||||
loader.abort();
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
if (cmd.equals("load")) {
|
||||
if (loader == null) {
|
||||
try {
|
||||
latitude = lat.get_value();
|
||||
longitude = lon.get_value();
|
||||
int min_z = (Integer) min_zoom.getSelectedItem();
|
||||
int max_z = (Integer) max_zoom.getSelectedItem();
|
||||
if (max_z < min_z)
|
||||
max_z = min_z;
|
||||
Double r = (Double) radius.getSelectedItem();
|
||||
|
||||
if (AltosPreferences.imperial_units())
|
||||
r = AltosConvert.miles_to_meters(r);
|
||||
else
|
||||
r = r * 1000;
|
||||
|
||||
center_map(latitude, longitude);
|
||||
|
||||
loader = new AltosMapLoader(this,
|
||||
latitude, longitude,
|
||||
min_z, max_z, r,
|
||||
all_types(), scale);
|
||||
|
||||
} catch (ParseException pe) {
|
||||
load_button.setSelected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void notify_launch_sites(final java.util.List<AltosLaunchSite> sites) {
|
||||
this.sites = sites;
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
int i = 1;
|
||||
for (AltosLaunchSite site : sites) {
|
||||
site_list.insertItemAt(site, i);
|
||||
i++;
|
||||
}
|
||||
reset_marks();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void set_radius_values() {
|
||||
radius_label.setText(String.format("Map Radius (%s)",
|
||||
AltosPreferences.imperial_units() ? "mi" : "km"));
|
||||
|
||||
Double[] radii;
|
||||
|
||||
if (AltosPreferences.imperial_units())
|
||||
radii = radius_mi;
|
||||
else
|
||||
radii = radius_km;
|
||||
|
||||
radius.removeAllItems();
|
||||
for (Double r : radii) {
|
||||
radius.addItem(r);
|
||||
}
|
||||
radius.setSelectedItem(radii[2]);
|
||||
radius.setMaximumRowCount(radii.length);
|
||||
}
|
||||
|
||||
public void units_changed(boolean imperial_units) {
|
||||
map.units_changed(imperial_units);
|
||||
set_radius_values();
|
||||
}
|
||||
|
||||
public void font_size_changed(int font_size) {
|
||||
map.font_size_changed(font_size);
|
||||
}
|
||||
|
||||
public AltosUIMapPreload(AltosUIFrame in_owner) {
|
||||
owner = in_owner;
|
||||
|
||||
Container pane = getScrollablePane();
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
Insets i = new Insets(4,4,4,4);
|
||||
|
||||
setTitle("AltOS Load Maps");
|
||||
|
||||
pane.setLayout(new GridBagLayout());
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
AltosUIPreferences.unregister_font_listener(AltosUIMapPreload.this);
|
||||
AltosPreferences.unregister_units_listener(AltosUIMapPreload.this);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
AltosPreferences.register_units_listener(this);
|
||||
AltosUIPreferences.register_font_listener(this);
|
||||
|
||||
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<AltosLaunchSite>(new AltosLaunchSite[] { new AltosLaunchSite("Site List", 0, 0) });
|
||||
site_list.addItemListener(this);
|
||||
|
||||
new AltosLaunchSites(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, this,
|
||||
"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, this,
|
||||
"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 = AltosMap.maptype_hybrid; type <= AltosMap.maptype_terrain; type++) {
|
||||
maptypes[type] = new JCheckBox(AltosMap.maptype_labels[type],
|
||||
type == AltosMap.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);
|
||||
|
||||
radius_label = new JLabel();
|
||||
|
||||
c.gridx = 4;
|
||||
c.gridy = 4;
|
||||
pane.add(radius_label, c);
|
||||
|
||||
radius = new JComboBox<Double>();
|
||||
radius.setEditable(true);
|
||||
c.gridx = 5;
|
||||
c.gridy = 4;
|
||||
pane.add(radius, c);
|
||||
|
||||
set_radius_values();
|
||||
|
||||
pack();
|
||||
setLocationRelativeTo(owner);
|
||||
setVisible(true);
|
||||
}
|
||||
}
|
||||
115
altosuilib/AltosUIMarker.java
Normal file
115
altosuilib/AltosUIMarker.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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.*;
|
||||
|
||||
public class AltosUIMarker implements AltosUIGrapher {
|
||||
ArrayList<ValueMarker> markers;
|
||||
int last_id;
|
||||
XYPlot plot;
|
||||
boolean enabled;
|
||||
int fetch;
|
||||
Color color;
|
||||
|
||||
private void remove_markers() {
|
||||
for (ValueMarker marker : markers)
|
||||
plot.removeDomainMarker(marker);
|
||||
}
|
||||
|
||||
private void add_markers() {
|
||||
for (ValueMarker marker : markers)
|
||||
plot.addDomainMarker(marker);
|
||||
}
|
||||
|
||||
public void set_units() {
|
||||
}
|
||||
|
||||
public boolean need_reset() { return true; }
|
||||
|
||||
public void set_enable(boolean enable) {
|
||||
if (enabled == enable)
|
||||
return;
|
||||
if (enable)
|
||||
add_markers();
|
||||
else
|
||||
remove_markers();
|
||||
enabled = enable;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (enabled)
|
||||
remove_markers();
|
||||
markers = new ArrayList<ValueMarker>();
|
||||
}
|
||||
|
||||
public void add(AltosUIDataPoint dataPoint) {
|
||||
try {
|
||||
int id = dataPoint.id(fetch);
|
||||
if (id < 0)
|
||||
return;
|
||||
if (id == last_id)
|
||||
return;
|
||||
ValueMarker marker = new ValueMarker(dataPoint.x());
|
||||
marker.setLabel(dataPoint.id_name(fetch));
|
||||
marker.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
|
||||
marker.setLabelTextAnchor(TextAnchor.TOP_LEFT);
|
||||
marker.setPaint(color);
|
||||
if (enabled)
|
||||
plot.addDomainMarker(marker);
|
||||
markers.add(marker);
|
||||
last_id = id;
|
||||
} catch (AltosUIDataMissing m) {
|
||||
}
|
||||
}
|
||||
|
||||
public AltosUIMarker (int fetch, Color color, XYPlot plot, boolean enable) {
|
||||
markers = new ArrayList<ValueMarker>();
|
||||
last_id = -1;
|
||||
this.fetch = fetch;
|
||||
this.color = color;
|
||||
this.plot = plot;
|
||||
this.enabled = enable;
|
||||
}
|
||||
|
||||
public void setNotify(boolean notify) {
|
||||
}
|
||||
|
||||
public void fireSeriesChanged() {
|
||||
}
|
||||
|
||||
public AltosUIMarker (int fetch, Color color, XYPlot plot) {
|
||||
this(fetch, color, plot, true);
|
||||
}
|
||||
}
|
||||
223
altosuilib/AltosUIPreferences.java
Normal file
223
altosuilib/AltosUIPreferences.java
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.awt.Component;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosUIPreferences extends AltosPreferences {
|
||||
|
||||
/* font size preferences name */
|
||||
final static String fontSizePreference = "FONT-SIZE";
|
||||
|
||||
/* Look&Feel preference name */
|
||||
final static String lookAndFeelPreference = "LOOK-AND-FEEL";
|
||||
|
||||
/* Window position preference name */
|
||||
final static String positionPreference = "POSITION";
|
||||
|
||||
/* Maps cache size preference name */
|
||||
final static String mapCachePreference = "MAP-CACHE";
|
||||
|
||||
/* UI Component to pop dialogs up */
|
||||
static Component component;
|
||||
|
||||
static LinkedList<AltosFontListener> font_listeners;
|
||||
|
||||
static int font_size = AltosUILib.font_size_medium;
|
||||
|
||||
static LinkedList<AltosUIListener> ui_listeners;
|
||||
|
||||
static String look_and_feel = null;
|
||||
|
||||
/* Serial debug */
|
||||
public static boolean serial_debug;
|
||||
|
||||
static LinkedList<AltosPositionListener> position_listeners;
|
||||
|
||||
public static int position = AltosUILib.position_top_left;
|
||||
|
||||
public static void init() {
|
||||
AltosPreferences.init(new AltosUIPreferencesBackend());
|
||||
|
||||
font_listeners = new LinkedList<AltosFontListener>();
|
||||
|
||||
font_size = backend.getInt(fontSizePreference, AltosUILib.font_size_medium);
|
||||
AltosUILib.set_fonts(font_size);
|
||||
look_and_feel = backend.getString(lookAndFeelPreference, UIManager.getSystemLookAndFeelClassName());
|
||||
|
||||
ui_listeners = new LinkedList<AltosUIListener>();
|
||||
serial_debug = backend.getBoolean(serialDebugPreference, false);
|
||||
|
||||
AltosLink.set_debug(serial_debug);
|
||||
|
||||
position = backend.getInt(positionPreference, AltosUILib.position_top_left);
|
||||
position_listeners = new LinkedList<AltosPositionListener>();
|
||||
}
|
||||
|
||||
static { init(); }
|
||||
|
||||
public static void set_component(Component in_component) {
|
||||
component = in_component;
|
||||
}
|
||||
|
||||
private static boolean check_dir(File dir) {
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkdirs()) {
|
||||
JOptionPane.showMessageDialog(component,
|
||||
dir.getName(),
|
||||
"Cannot create directory",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
} else if (!dir.isDirectory()) {
|
||||
JOptionPane.showMessageDialog(component,
|
||||
dir.getName(),
|
||||
"Is not a directory",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Configure the log directory. This is where all telemetry and eeprom files
|
||||
* will be written to, and where replay will look for telemetry files
|
||||
*/
|
||||
public static void ConfigureLog() {
|
||||
JFileChooser logdir_chooser = new JFileChooser(logdir.getParentFile());
|
||||
|
||||
logdir_chooser.setDialogTitle("Configure Data Logging Directory");
|
||||
logdir_chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
|
||||
if (logdir_chooser.showDialog(component, "Select Directory") == JFileChooser.APPROVE_OPTION) {
|
||||
File dir = logdir_chooser.getSelectedFile();
|
||||
if (check_dir(dir))
|
||||
set_logdir(dir);
|
||||
}
|
||||
}
|
||||
public static int font_size() {
|
||||
synchronized (backend) {
|
||||
return font_size;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_fonts() {
|
||||
}
|
||||
|
||||
public static void set_font_size(int new_font_size) {
|
||||
synchronized (backend) {
|
||||
font_size = new_font_size;
|
||||
backend.putInt(fontSizePreference, font_size);
|
||||
flush_preferences();
|
||||
AltosUILib.set_fonts(font_size);
|
||||
for (AltosFontListener l : font_listeners) {
|
||||
l.font_size_changed(font_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void register_font_listener(AltosFontListener l) {
|
||||
synchronized (backend) {
|
||||
font_listeners.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
public static void unregister_font_listener(AltosFontListener l) {
|
||||
synchronized (backend) {
|
||||
font_listeners.remove(l);
|
||||
}
|
||||
}
|
||||
|
||||
public static void set_look_and_feel(String new_look_and_feel) {
|
||||
try {
|
||||
UIManager.setLookAndFeel(new_look_and_feel);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
synchronized(backend) {
|
||||
look_and_feel = new_look_and_feel;
|
||||
backend.putString(lookAndFeelPreference, look_and_feel);
|
||||
flush_preferences();
|
||||
for (AltosUIListener l : ui_listeners)
|
||||
l.ui_changed(look_and_feel);
|
||||
}
|
||||
}
|
||||
|
||||
public static String look_and_feel() {
|
||||
synchronized (backend) {
|
||||
return look_and_feel;
|
||||
}
|
||||
}
|
||||
|
||||
public static void register_ui_listener(AltosUIListener l) {
|
||||
synchronized(backend) {
|
||||
ui_listeners.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
public static void unregister_ui_listener(AltosUIListener l) {
|
||||
synchronized (backend) {
|
||||
ui_listeners.remove(l);
|
||||
}
|
||||
}
|
||||
public static void set_serial_debug(boolean new_serial_debug) {
|
||||
AltosLink.set_debug(new_serial_debug);
|
||||
synchronized (backend) {
|
||||
serial_debug = new_serial_debug;
|
||||
backend.putBoolean(serialDebugPreference, serial_debug);
|
||||
flush_preferences();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean serial_debug() {
|
||||
synchronized (backend) {
|
||||
return serial_debug;
|
||||
}
|
||||
}
|
||||
|
||||
public static void register_position_listener(AltosPositionListener l) {
|
||||
synchronized(backend) {
|
||||
position_listeners.add(l);
|
||||
}
|
||||
}
|
||||
|
||||
public static void unregister_position_listener(AltosPositionListener l) {
|
||||
synchronized (backend) {
|
||||
position_listeners.remove(l);
|
||||
}
|
||||
}
|
||||
|
||||
public static void set_position(int new_position) {
|
||||
synchronized (backend) {
|
||||
position = new_position;
|
||||
backend.putInt(positionPreference, position);
|
||||
flush_preferences();
|
||||
for (AltosPositionListener l : position_listeners)
|
||||
l.position_changed(position);
|
||||
}
|
||||
}
|
||||
|
||||
public static int position() {
|
||||
synchronized (backend) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
altosuilib/AltosUIPreferencesBackend.java
Normal file
114
altosuilib/AltosUIPreferencesBackend.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.prefs.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
import javax.swing.filechooser.FileSystemView;
|
||||
|
||||
public class AltosUIPreferencesBackend extends AltosPreferencesBackend {
|
||||
|
||||
private Preferences _preferences = null;
|
||||
|
||||
public AltosUIPreferencesBackend() {
|
||||
_preferences = Preferences.userRoot().node("/org/altusmetrum/altosui");
|
||||
}
|
||||
|
||||
public AltosUIPreferencesBackend(Preferences in_preferences) {
|
||||
_preferences = in_preferences;
|
||||
}
|
||||
|
||||
public String getString(String key, String def) {
|
||||
return _preferences.get(key, def);
|
||||
}
|
||||
public void putString(String key, String value) {
|
||||
_preferences.put(key, value);
|
||||
}
|
||||
|
||||
public int getInt(String key, int def) {
|
||||
return _preferences.getInt(key, def);
|
||||
}
|
||||
public void putInt(String key, int value) {
|
||||
_preferences.putInt(key, value);
|
||||
}
|
||||
|
||||
public double getDouble(String key, double def) {
|
||||
return _preferences.getDouble(key, def);
|
||||
}
|
||||
public void putDouble(String key, double value) {
|
||||
_preferences.putDouble(key, value);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key, boolean def) {
|
||||
return _preferences.getBoolean(key, def);
|
||||
}
|
||||
public void putBoolean(String key, boolean value) {
|
||||
_preferences.putBoolean(key, value);
|
||||
}
|
||||
|
||||
public byte[] getBytes(String key, byte[] def) {
|
||||
return _preferences.getByteArray(key, def);
|
||||
}
|
||||
|
||||
public void putBytes(String key, byte[] value) {
|
||||
_preferences.putByteArray(key, value);
|
||||
}
|
||||
|
||||
public boolean nodeExists(String key) {
|
||||
try {
|
||||
return _preferences.nodeExists(key);
|
||||
} catch (BackingStoreException be) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosPreferencesBackend node(String key) {
|
||||
return new AltosUIPreferencesBackend(_preferences.node(key));
|
||||
}
|
||||
|
||||
public String[] keys() {
|
||||
try {
|
||||
return _preferences.keys();
|
||||
} catch (BackingStoreException be) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
_preferences.remove(key);
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
try {
|
||||
_preferences.flush();
|
||||
} catch (BackingStoreException ee) {
|
||||
System.err.printf("Cannot save preferences\n");
|
||||
}
|
||||
}
|
||||
|
||||
public File homeDirectory() {
|
||||
/* Use the file system view default directory */
|
||||
return FileSystemView.getFileSystemView().getDefaultDirectory();
|
||||
}
|
||||
|
||||
public void debug(String format, Object ... arguments) {
|
||||
System.out.printf(format, arguments);
|
||||
}
|
||||
}
|
||||
61
altosuilib/AltosUIRateList.java
Normal file
61
altosuilib/AltosUIRateList.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosUIRateList extends JComboBox<String> {
|
||||
|
||||
String product;
|
||||
int serial;
|
||||
|
||||
public void set_rate(int new_rate) {
|
||||
if (new_rate != AltosLib.MISSING)
|
||||
setSelectedIndex(new_rate);
|
||||
setVisible(new_rate != AltosLib.MISSING);
|
||||
}
|
||||
|
||||
public void set_product(String new_product) {
|
||||
product = new_product;
|
||||
}
|
||||
|
||||
public void set_serial(int new_serial) {
|
||||
serial = new_serial;
|
||||
}
|
||||
|
||||
public int rate() {
|
||||
return getSelectedIndex();
|
||||
}
|
||||
|
||||
public AltosUIRateList () {
|
||||
super();
|
||||
for (int i = 0; i < AltosLib.ao_telemetry_rate_values.length; i++)
|
||||
addItem(String.format("%d baud", AltosLib.ao_telemetry_rate_values[i]));
|
||||
setMaximumRowCount(getItemCount());
|
||||
setEditable(false);
|
||||
product = "Unknown";
|
||||
serial = 0;
|
||||
}
|
||||
|
||||
public AltosUIRateList(int in_rate) {
|
||||
this();
|
||||
set_rate(in_rate);
|
||||
}
|
||||
}
|
||||
48
altosuilib/AltosUITelemetryList.java
Normal file
48
altosuilib/AltosUITelemetryList.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.util.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
|
||||
public class AltosUITelemetryList extends JComboBox<String> {
|
||||
public int get_selected() {
|
||||
return getSelectedIndex() + 1;
|
||||
}
|
||||
|
||||
public void set_selected(int telemetry) {
|
||||
setSelectedIndex(telemetry-1);
|
||||
}
|
||||
|
||||
public AltosUITelemetryList(int serial) {
|
||||
super();
|
||||
for (int i = AltosLib.ao_telemetry_min; i <= AltosLib.ao_telemetry_max; i++)
|
||||
addItem(AltosLib.telemetry_name(i));
|
||||
|
||||
int telemetry = AltosPreferences.telemetry(serial);
|
||||
if (telemetry < AltosLib.ao_telemetry_min || AltosLib.ao_telemetry_max < telemetry)
|
||||
telemetry = AltosLib.ao_telemetry_standard;
|
||||
setMaximumRowCount(AltosLib.ao_telemetry_max);
|
||||
set_selected(telemetry);
|
||||
revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
225
altosuilib/AltosUITimeSeries.java
Normal file
225
altosuilib/AltosUITimeSeries.java
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright © 2017 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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 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"; }
|
||||
|
||||
public int show_fraction(int width, boolean imperial_units) {
|
||||
if (width < 5)
|
||||
return 0;
|
||||
return width - 5;
|
||||
}
|
||||
|
||||
public int say_fraction(boolean imperial_units) { return 0; }
|
||||
}
|
||||
|
||||
class AltosXYSeries extends XYSeries {
|
||||
|
||||
public AltosXYSeries(String label) {
|
||||
super(label);
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosUITimeSeries extends AltosTimeSeries implements AltosUIGrapher {
|
||||
AltosUILineStyle line_style;
|
||||
boolean enable;
|
||||
boolean custom_axis_set;
|
||||
AltosUIAxis axis;
|
||||
boolean marker;
|
||||
boolean marker_top;
|
||||
XYLineAndShapeRenderer renderer;
|
||||
XYPlot plot;
|
||||
AltosXYSeries xy_series;
|
||||
ArrayList<ValueMarker> markers;
|
||||
float width;
|
||||
|
||||
/* AltosUIGrapher interface */
|
||||
public boolean need_reset() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
}
|
||||
|
||||
public void add(AltosUIDataPoint dataPoint) {
|
||||
}
|
||||
|
||||
public void setNotify(boolean notify) {
|
||||
}
|
||||
|
||||
public void fireSeriesChanged() {
|
||||
}
|
||||
|
||||
public void set_data() {
|
||||
if (marker) {
|
||||
if (markers != null) {
|
||||
for (ValueMarker marker : markers)
|
||||
plot.removeDomainMarker(marker);
|
||||
}
|
||||
markers = new ArrayList<ValueMarker>();
|
||||
for (AltosTimeValue v : this) {
|
||||
String s = units.string_value(v.value);
|
||||
ValueMarker marker = new ValueMarker(v.time);
|
||||
marker.setLabel(s);
|
||||
if (marker_top) {
|
||||
marker.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
|
||||
marker.setLabelTextAnchor(TextAnchor.TOP_LEFT);
|
||||
} else {
|
||||
marker.setLabelAnchor(RectangleAnchor.BOTTOM_RIGHT);
|
||||
marker.setLabelTextAnchor(TextAnchor.BOTTOM_LEFT);
|
||||
}
|
||||
marker.setPaint(line_style.color);
|
||||
marker.setStroke(new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
|
||||
if (enable)
|
||||
plot.addDomainMarker(marker);
|
||||
markers.add(marker);
|
||||
}
|
||||
} else {
|
||||
xy_series.clear();
|
||||
|
||||
xy_series.setNotify(false);
|
||||
for (AltosTimeValue v : this) {
|
||||
double value = v.value;
|
||||
if (units != null)
|
||||
value = units.graph_value(value);
|
||||
xy_series.add(v.time, value);
|
||||
}
|
||||
xy_series.setNotify(true);
|
||||
}
|
||||
clear_changed();
|
||||
}
|
||||
|
||||
public void set_units() {
|
||||
axis.set_units();
|
||||
StandardXYToolTipGenerator ttg;
|
||||
|
||||
if (units != null) {
|
||||
String time_example = (new AltosUITime()).graph_format(7);
|
||||
String example = units.graph_format(7);
|
||||
|
||||
ttg = new StandardXYToolTipGenerator(String.format("{1}s: {2}%s ({0})",
|
||||
units.graph_units()),
|
||||
new java.text.DecimalFormat(time_example),
|
||||
new java.text.DecimalFormat(example));
|
||||
renderer.setBaseToolTipGenerator(ttg);
|
||||
}
|
||||
set_data();
|
||||
}
|
||||
|
||||
public AltosXYSeries xy_series() {
|
||||
return xy_series;
|
||||
}
|
||||
|
||||
public void set_enable(boolean enable) {
|
||||
if (this.enable != enable) {
|
||||
this.enable = enable;
|
||||
if (marker) {
|
||||
for (ValueMarker marker : markers) {
|
||||
if (enable)
|
||||
plot.addDomainMarker(marker);
|
||||
else
|
||||
plot.removeDomainMarker(marker);
|
||||
}
|
||||
} else {
|
||||
renderer.setSeriesVisible(0, enable);
|
||||
axis.set_enable(enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public BasicStroke(float width, int cap, int join, float miterlimit,
|
||||
// float dash[], float dash_phase)
|
||||
|
||||
public void set_line_width(float width) {
|
||||
this.width = width;
|
||||
if (markers != null) {
|
||||
for (ValueMarker marker : markers) {
|
||||
marker.setStroke(new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
|
||||
}
|
||||
} else {
|
||||
if (line_style.dash[0] == 0.0)
|
||||
renderer.setSeriesStroke(0, new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||||
else
|
||||
renderer.setSeriesStroke(0, new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10.0f, line_style.dash, 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
public void set_axis(AltosUILineStyle line_style, boolean enable, AltosUIAxis axis) {
|
||||
this.line_style = line_style;
|
||||
this.enable = enable;
|
||||
this.axis = axis;
|
||||
this.marker = false;
|
||||
this.width = 1.0f;
|
||||
|
||||
axis.ref(this.enable);
|
||||
|
||||
renderer = new XYLineAndShapeRenderer(true, false);
|
||||
renderer.setSeriesPaint(0, line_style.color);
|
||||
set_line_width(this.width);
|
||||
renderer.setSeriesVisible(0, enable);
|
||||
xy_series = new AltosXYSeries(label);
|
||||
}
|
||||
|
||||
public void set_marker(AltosUILineStyle line_style, boolean enable, XYPlot plot, boolean marker_top) {
|
||||
this.line_style = line_style;
|
||||
this.enable = enable;
|
||||
this.marker = true;
|
||||
this.plot = plot;
|
||||
this.marker_top = marker_top;
|
||||
}
|
||||
|
||||
public void set_shapes_visible(boolean shapes_visible) {
|
||||
renderer.setSeriesShapesVisible(0, shapes_visible);
|
||||
}
|
||||
|
||||
public AltosUITimeSeries(String label, AltosUnits units) {
|
||||
super(label, units);
|
||||
}
|
||||
|
||||
public AltosUITimeSeries(String label, AltosUnits units,
|
||||
AltosUILineStyle line_style, boolean enable,
|
||||
AltosUIAxis axis) {
|
||||
this(label, units);
|
||||
set_axis(line_style, enable, axis);
|
||||
}
|
||||
}
|
||||
134
altosuilib/AltosUIUnitsIndicator.java
Normal file
134
altosuilib/AltosUIUnitsIndicator.java
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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 boolean hide(AltosState state, AltosListenerState listener_state, int i) {
|
||||
return hide(state, i);
|
||||
}
|
||||
|
||||
public double value (AltosState state, AltosListenerState listener_state, int i) {
|
||||
return value(state, i);
|
||||
}
|
||||
|
||||
public double[] last_values;
|
||||
|
||||
private void show(boolean force, double... v) {
|
||||
show();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (force || 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean hide = false;
|
||||
|
||||
public void show(double... v) {
|
||||
show(false, v);
|
||||
}
|
||||
|
||||
public void units_changed(boolean imperial_units) {
|
||||
if (!hide)
|
||||
show(true, last_values);
|
||||
}
|
||||
|
||||
public void show (AltosState state, AltosListenerState listener_state) {
|
||||
double[] v = new double[values.length];
|
||||
|
||||
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, listener_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
43
altosuilib/AltosUIVoltageIndicator.java
Normal file
43
altosuilib/AltosUIVoltageIndicator.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
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 = AltosLib.MISSING;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
144
altosuilib/AltosUSBDevice.java
Normal file
144
altosuilib/AltosUSBDevice.java
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.util.*;
|
||||
import libaltosJNI.*;
|
||||
import org.altusmetrum.altoslib_14.*;
|
||||
|
||||
public class AltosUSBDevice extends altos_device implements AltosDevice {
|
||||
|
||||
public String toString() {
|
||||
String name = getName();
|
||||
if (name == null || "".equals(name))
|
||||
name = "Altus Metrum";
|
||||
return String.format("%-20.20s %4d %s",
|
||||
name, getSerial(), getPath());
|
||||
}
|
||||
|
||||
public String toShortString() {
|
||||
String name = getName();
|
||||
if (name == null || "".equals(name))
|
||||
name = "Altus Metrum";
|
||||
return String.format("%s %d %s",
|
||||
name, getSerial(), getPath());
|
||||
|
||||
}
|
||||
|
||||
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 SWIGTYPE_p_altos_file open() {
|
||||
return libaltos.altos_open(this);
|
||||
}
|
||||
|
||||
public boolean isAltusMetrum() {
|
||||
if (getVendor() != AltosUILib.vendor_altusmetrum)
|
||||
return false;
|
||||
if (getProduct() < AltosUILib.product_altusmetrum_min)
|
||||
return false;
|
||||
if (getProduct() > AltosUILib.product_altusmetrum_max)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean matchProduct(int want_product) {
|
||||
|
||||
if (!isAltusMetrum())
|
||||
return false;
|
||||
|
||||
if (want_product == AltosUILib.product_any)
|
||||
return true;
|
||||
|
||||
int have_product = getProduct();
|
||||
|
||||
if (want_product == AltosUILib.product_basestation)
|
||||
return have_product == AltosUILib.product_teledongle ||
|
||||
have_product == AltosUILib.product_telebt ||
|
||||
have_product == AltosUILib.product_megadongle;
|
||||
|
||||
if (want_product == AltosUILib.product_altimeter)
|
||||
return have_product == AltosUILib.product_telemetrum ||
|
||||
have_product == AltosUILib.product_telemega ||
|
||||
have_product == AltosUILib.product_easytimer ||
|
||||
have_product == AltosUILib.product_easymega ||
|
||||
have_product == AltosUILib.product_telegps ||
|
||||
have_product == AltosUILib.product_easymini ||
|
||||
have_product == AltosUILib.product_telemini;
|
||||
|
||||
if (have_product == AltosUILib.product_altusmetrum) /* old devices match any request */
|
||||
return true;
|
||||
|
||||
if (want_product == have_product)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return getVendor() ^ getProduct() ^ getSerial() ^ getPath().hashCode();
|
||||
}
|
||||
|
||||
public AltosUsbId usb_id() {
|
||||
return new AltosUsbId(getVendor(), getProduct());
|
||||
}
|
||||
|
||||
public String usb_product() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null)
|
||||
return false;
|
||||
|
||||
if (!(o instanceof AltosUSBDevice))
|
||||
return false;
|
||||
AltosUSBDevice other = (AltosUSBDevice) o;
|
||||
|
||||
return getVendor() == other.getVendor() &&
|
||||
getProduct() == other.getProduct() &&
|
||||
getSerial() == other.getSerial() &&
|
||||
getPath().equals(other.getPath());
|
||||
}
|
||||
|
||||
static public java.util.List<AltosDevice> list(int product) {
|
||||
if (!AltosUILib.load_library())
|
||||
return null;
|
||||
|
||||
SWIGTYPE_p_altos_list list = libaltos.altos_list_start();
|
||||
|
||||
ArrayList<AltosDevice> device_list = new ArrayList<AltosDevice>();
|
||||
if (list != null) {
|
||||
for (;;) {
|
||||
AltosUSBDevice device = new AltosUSBDevice();
|
||||
if (libaltos.altos_list_next(list, device) == 0)
|
||||
break;
|
||||
if (device.matchProduct(product))
|
||||
device_list.add(device);
|
||||
}
|
||||
libaltos.altos_list_finish(list);
|
||||
}
|
||||
|
||||
return device_list;
|
||||
}
|
||||
}
|
||||
95
altosuilib/AltosVoice.java
Normal file
95
altosuilib/AltosVoice.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
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)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
altosuilib/Makefile.am
Normal file
106
altosuilib/Makefile.am
Normal file
@@ -0,0 +1,106 @@
|
||||
AM_JAVACFLAGS=$(JAVAC_VERSION_FLAGS) -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked
|
||||
|
||||
JAVAROOT=bin
|
||||
|
||||
CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../altoslib/*:../libaltos:/usr/share/java/*"
|
||||
|
||||
SRC=.
|
||||
|
||||
altosuilibdir = $(datadir)/java
|
||||
|
||||
altosuilib_JAVA = \
|
||||
AltosDevice.java \
|
||||
AltosDeviceDialog.java \
|
||||
AltosPositionListener.java \
|
||||
AltosUIAccelCal.java \
|
||||
AltosUIConfigure.java \
|
||||
AltosUIAxis.java \
|
||||
AltosUIDataMissing.java \
|
||||
AltosUIDataPoint.java \
|
||||
AltosUIDataSet.java \
|
||||
AltosUIGrapher.java \
|
||||
AltosUIDialog.java \
|
||||
AltosUIEnable.java \
|
||||
AltosUIFrame.java \
|
||||
AltosUILib.java \
|
||||
AltosUIListener.java \
|
||||
AltosUIMarker.java \
|
||||
AltosUIPreferencesBackend.java \
|
||||
AltosUIPreferences.java \
|
||||
AltosUIFlightSeries.java \
|
||||
AltosUIGraph.java \
|
||||
AltosGraph.java \
|
||||
AltosShapeListener.java \
|
||||
AltosUSBDevice.java \
|
||||
AltosVoice.java \
|
||||
AltosDisplayThread.java \
|
||||
AltosDeviceUIDialog.java \
|
||||
AltosSerial.java \
|
||||
AltosSerialInUseException.java \
|
||||
AltosConfigFreqUI.java \
|
||||
AltosScanUI.java \
|
||||
AltosEepromDelete.java \
|
||||
AltosEepromGrapher.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 \
|
||||
AltosBTDevice.java \
|
||||
AltosBTDeviceIterator.java \
|
||||
AltosBTManage.java \
|
||||
AltosBTKnown.java \
|
||||
AltosUILineStyle.java \
|
||||
AltosUIMap.java \
|
||||
AltosUIMapPreload.java \
|
||||
AltosUIFlightTab.java \
|
||||
AltosUIIndicator.java \
|
||||
AltosUIUnitsIndicator.java \
|
||||
AltosUIVoltageIndicator.java \
|
||||
AltosUIFreqList.java \
|
||||
AltosUITelemetryList.java \
|
||||
AltosUIRateList.java \
|
||||
AltosUIImage.java \
|
||||
AltosUITimeSeries.java \
|
||||
OSXAdapter.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:
|
||||
-rm -rf $(JAVAROOT) altosuilib_*.jar
|
||||
|
||||
install-altosuilibJAVA: $(JAR)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(altosuilibdir)" || $(MKDIR_P) "$(DESTDIR)$(altosuilibdir)"
|
||||
echo " $(INSTALL_DATA)" "$(JAR)" "'$(DESTDIR)$(altosuilibdir)/$(JAR)"; \
|
||||
$(INSTALL_DATA) "$(JAR)" "$(DESTDIR)$(altosuilibdir)"
|
||||
|
||||
$(JAVAROOT):
|
||||
mkdir -p $(JAVAROOT)
|
||||
|
||||
$(JAR): classaltosuilib.stamp $(ICONS)
|
||||
jar cf $@ $(ICONJAR) -C $(JAVAROOT) .
|
||||
if STRIP_NONDETERMINISM
|
||||
$(STRIP_NONDETERMINISM) $@
|
||||
endif
|
||||
206
altosuilib/OSXAdapter.java
Executable file
206
altosuilib/OSXAdapter.java
Executable file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
|
||||
File: OSXAdapter.java
|
||||
|
||||
Abstract: Hooks existing preferences/about/quit functionality from an
|
||||
existing Java app into handlers for the Mac OS X application menu.
|
||||
Uses a Proxy object to dynamically implement the
|
||||
com.apple.eawt.ApplicationListener interface and register it with the
|
||||
com.apple.eawt.Application object. This allows the complete project
|
||||
to be both built and run on any platform without any stubs or
|
||||
placeholders. Useful for developers looking to implement Mac OS X
|
||||
features while supporting multiple platforms with minimal impact.
|
||||
|
||||
Version: 2.0
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by
|
||||
Apple Inc. ("Apple") in consideration of your agreement to the
|
||||
following terms, and your use, installation, modification or
|
||||
redistribution of this Apple software constitutes acceptance of these
|
||||
terms. If you do not agree with these terms, please do not use,
|
||||
install, modify or redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and
|
||||
subject to these terms, Apple grants you a personal, non-exclusive
|
||||
license, under Apple's copyrights in this original Apple software (the
|
||||
"Apple Software"), to use, reproduce, modify and redistribute the Apple
|
||||
Software, with or without modifications, in source and/or binary forms;
|
||||
provided that if you redistribute the Apple Software in its entirety and
|
||||
without modifications, you must retain this notice and the following
|
||||
text and disclaimers in all such redistributions of the Apple Software.
|
||||
Neither the name, trademarks, service marks or logos of Apple Inc.
|
||||
may be used to endorse or promote products derived from the Apple
|
||||
Software without specific prior written permission from Apple. Except
|
||||
as expressly stated in this notice, no other rights or licenses, express
|
||||
or implied, are granted by Apple herein, including but not limited to
|
||||
any patent rights that may be infringed by your derivative works or by
|
||||
other works in which the Apple Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
|
||||
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
||||
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
|
||||
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
|
||||
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
|
||||
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
|
||||
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Copyright © 2003-2007 Apple, Inc., All Rights Reserved
|
||||
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altosuilib_14;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
public class OSXAdapter implements InvocationHandler {
|
||||
|
||||
protected Object targetObject;
|
||||
protected Method targetMethod;
|
||||
protected String proxySignature;
|
||||
|
||||
static Object macOSXApplication;
|
||||
|
||||
// Pass this method an Object and Method equipped to perform application shutdown logic
|
||||
// The method passed should return a boolean stating whether or not the quit should occur
|
||||
public static void setQuitHandler(Object target, Method quitHandler) {
|
||||
setHandler(new OSXAdapter("handleQuit", target, quitHandler));
|
||||
}
|
||||
|
||||
// Pass this method an Object and Method equipped to display application info
|
||||
// They will be called when the About menu item is selected from the application menu
|
||||
public static void setAboutHandler(Object target, Method aboutHandler) {
|
||||
boolean enableAboutMenu = (target != null && aboutHandler != null);
|
||||
if (enableAboutMenu) {
|
||||
setHandler(new OSXAdapter("handleAbout", target, aboutHandler));
|
||||
}
|
||||
// If we're setting a handler, enable the About menu item by calling
|
||||
// com.apple.eawt.Application reflectively
|
||||
try {
|
||||
Method enableAboutMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu", new Class[] { boolean.class });
|
||||
enableAboutMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enableAboutMenu) });
|
||||
} catch (Exception ex) {
|
||||
System.err.println("OSXAdapter could not access the About Menu");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Pass this method an Object and a Method equipped to display application options
|
||||
// They will be called when the Preferences menu item is selected from the application menu
|
||||
public static void setPreferencesHandler(Object target, Method prefsHandler) {
|
||||
boolean enablePrefsMenu = (target != null && prefsHandler != null);
|
||||
if (enablePrefsMenu) {
|
||||
setHandler(new OSXAdapter("handlePreferences", target, prefsHandler));
|
||||
}
|
||||
// If we're setting a handler, enable the Preferences menu item by calling
|
||||
// com.apple.eawt.Application reflectively
|
||||
try {
|
||||
Method enablePrefsMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class });
|
||||
enablePrefsMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enablePrefsMenu) });
|
||||
} catch (Exception ex) {
|
||||
System.err.println("OSXAdapter could not access the About Menu");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Pass this method an Object and a Method equipped to handle document events from the Finder
|
||||
// Documents are registered with the Finder via the CFBundleDocumentTypes dictionary in the
|
||||
// application bundle's Info.plist
|
||||
public static void setFileHandler(Object target, Method fileHandler) {
|
||||
setHandler(new OSXAdapter("handleOpenFile", target, fileHandler) {
|
||||
// Override OSXAdapter.callTarget to send information on the
|
||||
// file to be opened
|
||||
public boolean callTarget(Object appleEvent) {
|
||||
if (appleEvent != null) {
|
||||
try {
|
||||
Method getFilenameMethod = appleEvent.getClass().getDeclaredMethod("getFilename", (Class[])null);
|
||||
String filename = (String) getFilenameMethod.invoke(appleEvent, (Object[])null);
|
||||
this.targetMethod.invoke(this.targetObject, new Object[] { filename });
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// setHandler creates a Proxy object from the passed OSXAdapter and adds it as an ApplicationListener
|
||||
public static void setHandler(OSXAdapter adapter) {
|
||||
try {
|
||||
Class applicationClass = Class.forName("com.apple.eawt.Application");
|
||||
if (macOSXApplication == null) {
|
||||
macOSXApplication = applicationClass.getConstructor((Class[])null).newInstance((Object[])null);
|
||||
}
|
||||
Class applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener");
|
||||
Method addListenerMethod = applicationClass.getDeclaredMethod("addApplicationListener", new Class[] { applicationListenerClass });
|
||||
// Create a proxy object around this handler that can be reflectively added as an Apple ApplicationListener
|
||||
Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(), new Class[] { applicationListenerClass }, adapter);
|
||||
addListenerMethod.invoke(macOSXApplication, new Object[] { osxAdapterProxy });
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
System.err.println("This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled (" + cnfe + ")");
|
||||
} catch (Exception ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking eawt.Application methods
|
||||
System.err.println("Mac OS X Adapter could not talk to EAWT:");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Each OSXAdapter has the name of the EAWT method it intends to listen for (handleAbout, for example),
|
||||
// the Object that will ultimately perform the task, and the Method to be called on that Object
|
||||
protected OSXAdapter(String proxySignature, Object target, Method handler) {
|
||||
this.proxySignature = proxySignature;
|
||||
this.targetObject = target;
|
||||
this.targetMethod = handler;
|
||||
}
|
||||
|
||||
// Override this method to perform any operations on the event
|
||||
// that comes with the various callbacks
|
||||
// See setFileHandler above for an example
|
||||
public boolean callTarget(Object appleEvent) throws InvocationTargetException, IllegalAccessException {
|
||||
Object result = targetMethod.invoke(targetObject, (Object[])null);
|
||||
if (result == null) {
|
||||
return true;
|
||||
}
|
||||
return Boolean.valueOf(result.toString()).booleanValue();
|
||||
}
|
||||
|
||||
// InvocationHandler implementation
|
||||
// This is the entry point for our proxy object; it is called every time an ApplicationListener method is invoked
|
||||
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (isCorrectMethod(method, args)) {
|
||||
boolean handled = callTarget(args[0]);
|
||||
setApplicationEventHandled(args[0], handled);
|
||||
}
|
||||
// All of the ApplicationListener methods are void; return null regardless of what happens
|
||||
return null;
|
||||
}
|
||||
|
||||
// Compare the method that was called to the intended method when the OSXAdapter instance was created
|
||||
// (e.g. handleAbout, handleQuit, handleOpenFile, etc.)
|
||||
protected boolean isCorrectMethod(Method method, Object[] args) {
|
||||
return (targetMethod != null && proxySignature.equals(method.getName()) && args.length == 1);
|
||||
}
|
||||
|
||||
// It is important to mark the ApplicationEvent as handled and cancel the default behavior
|
||||
// This method checks for a boolean result from the proxy method and sets the event accordingly
|
||||
protected void setApplicationEventHandled(Object event, boolean handled) {
|
||||
if (event != null) {
|
||||
try {
|
||||
Method setHandledMethod = event.getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class });
|
||||
// If the target method returns a boolean, use that as a hint
|
||||
setHandledMethod.invoke(event, new Object[] { Boolean.valueOf(handled) });
|
||||
} catch (Exception ex) {
|
||||
System.err.println("OSXAdapter was unable to handle an ApplicationEvent: " + event);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user