Add lots more aoview UI bits
authorKeith Packard <keithp@keithp.com>
Sun, 17 May 2009 07:13:45 +0000 (00:13 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 17 May 2009 07:13:45 +0000 (00:13 -0700)
Logs data to files, displays current state in window.

Signed-off-by: Keith Packard <keithp@keithp.com>
12 files changed:
aoview/.gitignore [new file with mode: 0644]
aoview/Makefile
aoview/aoview.glade
aoview/aoview.h
aoview/aoview_dev.c
aoview/aoview_dev_dialog.c
aoview/aoview_log.c [new file with mode: 0644]
aoview/aoview_main.c
aoview/aoview_monitor.c
aoview/aoview_state.c
aoview/aoview_table.c [new file with mode: 0644]
aoview/aoview_util.c [new file with mode: 0644]

diff --git a/aoview/.gitignore b/aoview/.gitignore
new file mode 100644 (file)
index 0000000..03c0c7d
--- /dev/null
@@ -0,0 +1,2 @@
+*.o
+aoview
index bdf7853..253a160 100644 (file)
@@ -1,4 +1,4 @@
-MODULES=gtk+-2.0 libglade-2.0
+MODULES=gtk+-2.0 libglade-2.0 gconf-2.0
 INCLUDES=$(shell pkg-config --cflags $(MODULES)) -I..
 WARN= -Wall -Wpointer-arith -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -fno-strict-aliasing
 CFLAGS=$(INCLUDES) -O0 -g $(WARN)
@@ -11,7 +11,10 @@ SRC = \
        aoview_serial.c \
        aoview_monitor.c \
        aoview_state.c \
-       aoview_convert.c
+       aoview_convert.c \
+       aoview_log.c \
+       aoview_table.c \
+       aoview_util.c
 
 INC = \
        aoview.h
index e60f17f..4b1566d 100644 (file)
                 </child>
               </widget>
             </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem5">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Log</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu5">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="ao_log_new">
+                        <property name="label" translatable="yes">_New log</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">False</property>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="image3">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-new</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="ao_log_configure">
+                        <property name="label" translatable="yes">_Configure Log</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" handler="gtk_widget_show" object="log_chooser_dialog" after="yes"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="image4">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-preferences</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
             <child>
               <widget class="GtkMenuItem" id="menuitem4">
                 <property name="visible">True</property>
           </packing>
         </child>
         <child>
-          <widget class="GtkTable" id="table1">
+          <widget class="GtkTreeView" id="dataview">
             <property name="visible">True</property>
-            <property name="n_rows">7</property>
-            <property name="n_columns">2</property>
-            <child>
-              <widget class="GtkLabel" id="altitude_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Altitude</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label2">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Velocity</property>
-              </widget>
-              <packing>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label3">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Acceleration</property>
-              </widget>
-              <packing>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label4">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Latitude</property>
-              </widget>
-              <packing>
-                <property name="top_attach">3</property>
-                <property name="bottom_attach">4</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label5">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Longitude</property>
-              </widget>
-              <packing>
-                <property name="top_attach">4</property>
-                <property name="bottom_attach">5</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label6">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Range</property>
-              </widget>
-              <packing>
-                <property name="top_attach">5</property>
-                <property name="bottom_attach">6</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="label7">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Direction</property>
-              </widget>
-              <packing>
-                <property name="top_attach">6</property>
-                <property name="bottom_attach">7</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="altitude_value">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">0m</property>
-                <property name="selectable">True</property>
-                <property name="single_line_mode">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="vel_value">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">150m/s</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="accel_value">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">50m/s&#xB2;</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">2</property>
-                <property name="bottom_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="lat_value">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">45&#xB0; 31' 24''</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">3</property>
-                <property name="bottom_attach">4</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="lon_value">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">122&#xB0; 40' 34''W</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">4</property>
-                <property name="bottom_attach">5</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="range_value">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">500m</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">5</property>
-                <property name="bottom_attach">6</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="direction_value">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">45&#xB0;</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">6</property>
-                <property name="bottom_attach">7</property>
-              </packing>
-            </child>
+            <property name="can_focus">True</property>
+            <property name="headers_clickable">False</property>
+            <property name="show_expanders">False</property>
+            <property name="enable_grid_lines">both</property>
           </widget>
           <packing>
             <property name="position">1</property>
             <property name="visible">True</property>
             <property name="layout_style">end</property>
             <child>
-              <widget class="GtkButton" id="connect_button">
-                <property name="label" translatable="yes">gtk-connect</property>
+              <widget class="GtkButton" id="cancel_button">
+                <property name="label" translatable="yes">gtk-cancel</property>
+                <property name="response_id">1</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
                 <property name="use_underline">True</property>
                 <property name="use_stock">True</property>
+                <signal name="clicked" handler="gtk_widget_hide" object="device_connect_dialog" after="yes"/>
               </widget>
               <packing>
                 <property name="expand">False</property>
               </packing>
             </child>
             <child>
-              <widget class="GtkButton" id="cancel_button">
+              <widget class="GtkButton" id="connect_button">
+                <property name="label" translatable="yes">gtk-connect</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkFileChooserDialog" id="log_chooser_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Configure Log Directory</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <property name="action">select-folder</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox2">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area2">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <widget class="GtkButton" id="log_configure_cancel">
                 <property name="label" translatable="yes">gtk-cancel</property>
-                <property name="response_id">1</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
-                <property name="use_underline">True</property>
                 <property name="use_stock">True</property>
-                <signal name="clicked" handler="gtk_widget_hide" object="device_connect_dialog" after="yes"/>
+                <signal name="clicked" handler="gtk_widget_hide" object="log_chooser_dialog"/>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="log_configure_ok">
+                <property name="label" translatable="yes">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
               </widget>
               <packing>
                 <property name="expand">False</property>
       </widget>
     </child>
   </widget>
+  <widget class="GtkMessageDialog" id="log_fail_dialog">
+    <property name="border_width">5</property>
+    <property name="type">popup</property>
+    <property name="type_hint">normal</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="message_type">error</property>
+    <property name="buttons">close</property>
+    <property name="text">Cannot create log file</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox4">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area4">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
 </glade-interface>
index 028b2f1..a321412 100644 (file)
@@ -18,8 +18,7 @@
 #ifndef _AOVIEW_H_
 #define _AOVIEW_H_
 
-#include <gtk/gtk.h>
-#include <glade/glade.h>
+#define _GNU_SOURCE
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <assert.h>
 
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <gconf/gconf-client.h>
+
 struct usbdev {
        char    *sys;
        char    *tty;
@@ -115,4 +118,41 @@ aoview_pres_to_altitude(int16_t pres);
 int16_t
 aoview_altitude_to_pres(int16_t alt);
 
+char *
+aoview_fullname (char *dir, char *file);
+
+char *
+aoview_basename(char *file);
+
+GtkTreeViewColumn *
+aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width);
+
+int
+aoview_mkdir(char *dir);
+
+void
+aoview_log_init(GladeXML *xml);
+
+void
+aoview_log_set_serial(int serial);
+
+int
+aoview_log_get_serial(void);
+
+void
+aoview_log_printf(char *format, ...);
+
+void
+aoview_table_start(void);
+
+void
+aoview_table_add_row(char *label, char *format, ...);
+
+void
+aoview_table_finish(void);
+
+void
+aoview_table_init(GladeXML *xml);
+
+
 #endif /* _AOVIEW_H_ */
index 5bb3cc9..a326ad7 100644 (file)
 #include <ctype.h>
 #include <dirent.h>
 
-static char *
-fullname (char *dir, char *file)
-{
-       char    *new;
-       int     dlen = strlen (dir);
-       int     flen = strlen (file);
-       int     slen = 0;
-
-       if (dir[dlen-1] != '/')
-               slen = 1;
-       new = malloc (dlen + slen + flen + 1);
-       if (!new)
-               return 0;
-       strcpy(new, dir);
-       if (slen)
-               strcat (new, "/");
-       strcat(new, file);
-       return new;
-}
-
 static char *
 load_string(char *dir, char *file)
 {
-       char    *full = fullname(dir, file);
+       char    *full = aoview_fullname(dir, file);
        char    line[4096];
        char    *r;
        FILE    *f;
@@ -79,17 +59,6 @@ load_hex(char *dir, char *file)
        return i;
 }
 
-static char *
-basename(char *file)
-{
-       char *b;
-
-       b = strrchr(file, '/');
-       if (!b)
-               return file;
-       return b + 1;
-}
-
 static int
 dir_filter_tty(const struct dirent *d)
 {
@@ -110,20 +79,20 @@ usb_tty(char *sys)
        int ntty;
        char *tty;
 
-       base = basename(sys);
+       base = aoview_basename(sys);
        num_configs = load_hex(sys, "bNumConfigurations");
        num_interfaces = load_hex(sys, "bNumInterfaces");
        for (config = 1; config <= num_configs; config++) {
                for (interface = 0; interface < num_interfaces; interface++) {
                        sprintf(endpoint_base, "%s:%d.%d",
                                base, config, interface);
-                       endpoint_full = fullname(sys, endpoint_base);
+                       endpoint_full = aoview_fullname(sys, endpoint_base);
                        ntty = scandir(endpoint_full, &namelist,
                                       dir_filter_tty,
                                       alphasort);
                        free(endpoint_full);
                        if (ntty) {
-                               tty = fullname("/dev", namelist[0]->d_name + 4);
+                               tty = aoview_fullname("/dev", namelist[0]->d_name + 4);
                                free(namelist);
                                return tty;
                        }
@@ -196,7 +165,7 @@ aoview_usb_scan(struct usbdev ***devs_ret)
        if (!n)
                return 0;
        for (e = 0; e < n; e++) {
-               dir = fullname(USB_DEVICES, ents[e]->d_name);
+               dir = aoview_fullname(USB_DEVICES, ents[e]->d_name);
                dev = usb_scan_device(dir);
                free(dir);
                if (dev->idVendor == 0xfffe && dev->tty) {
index f1165e1..0094740 100644 (file)
 
 #include "aoview.h"
 
-static GtkTreeViewColumn *
-add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width)
-{
-       GtkCellRenderer *renderer;
-       GtkTreeViewColumn *column;
-
-       renderer = gtk_cell_renderer_text_new ();
-       g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
-       g_object_set(renderer, "width-chars", width, NULL);
-       column = gtk_tree_view_column_new_with_attributes (title, renderer,
-                                                          "text", model_column,
-                                                          NULL);
-       gtk_tree_view_column_set_resizable (column, FALSE);
-       gtk_tree_view_append_column (view, column);
-
-       return column;
-}
-
-
-
 static void
 aoview_dev_dialog_map(GtkWidget *widget, gpointer data)
 {
@@ -118,9 +98,9 @@ aoview_dev_dialog_init(GladeXML *xml)
        dev_list = GTK_TREE_VIEW(glade_xml_get_widget(xml, "dev_list"));
        assert(dev_list);
 
-       add_plain_text_column(dev_list, _("Product"), 0, 16);
-       add_plain_text_column(dev_list, _("Serial"),  1, 8);
-       add_plain_text_column(dev_list, _("Device"), 2, 13);
+       aoview_add_plain_text_column(dev_list, _("Product"), 0, 16);
+       aoview_add_plain_text_column(dev_list, _("Serial"),  1, 8);
+       aoview_add_plain_text_column(dev_list, _("Device"), 2, 13);
 
        dev_selection = gtk_tree_view_get_selection(dev_list);
        gtk_tree_selection_set_mode(dev_selection, GTK_SELECTION_SINGLE);
diff --git a/aoview/aoview_log.c b/aoview/aoview_log.c
new file mode 100644 (file)
index 0000000..623c5aa
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+#define LOG_DIR_PATH   "/apps/aoview/log_dir"
+#define DEFAULT_LOG    "AltOS"
+
+static char *aoview_log_dir;
+static FILE *aoview_log_file;
+static int aoview_log_serial;
+static int aoview_log_sequence;
+static GtkMessageDialog *log_fail_dialog;
+static int aoview_log_failed;
+
+static void
+aoview_log_save_conf(void)
+{
+       GConfClient     *gconf_client;
+
+       gconf_client = gconf_client_get_default();
+       if (gconf_client)
+       {
+               gconf_client_set_string(gconf_client,
+                                       LOG_DIR_PATH,
+                                       aoview_log_dir,
+                                       NULL);
+               g_object_unref(G_OBJECT(gconf_client));
+       }
+}
+
+static void
+aoview_log_configure(GtkWidget *widget, gpointer data)
+{
+       GtkFileChooser *chooser = data;
+       aoview_log_dir = gtk_file_chooser_get_filename(chooser);
+       aoview_log_save_conf();
+       gtk_widget_hide(GTK_WIDGET(chooser));
+}
+
+static void
+aoview_log_new(void)
+{
+       if (aoview_log_file) {
+               fclose(aoview_log_file);
+               aoview_log_file = NULL;
+       }
+       aoview_log_failed = 0;
+}
+
+static void
+aoview_log_new_item(GtkWidget *widget, gpointer data)
+{
+       aoview_log_new();
+}
+
+void
+aoview_log_set_serial(int serial)
+{
+       aoview_log_serial = serial;
+}
+
+int
+aoview_log_get_serial(void)
+{
+       return aoview_log_serial;
+}
+
+static void
+aoview_log_open_failed(char *name)
+{
+       char    *utf8_file;
+       utf8_file = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
+       if (!utf8_file)
+               utf8_file = name;
+       gtk_message_dialog_format_secondary_text(log_fail_dialog,
+                                                "\"%s\"", utf8_file);
+       if (utf8_file != name)
+               g_free(utf8_file);
+       gtk_widget_show(GTK_WIDGET(log_fail_dialog));
+       aoview_log_failed = 1;
+}
+
+static void
+aoview_log_start(void)
+{
+       if (!aoview_log_file) {
+               char            base[50];
+               struct tm       tm;
+               time_t          now;
+               char            *full;
+               int             r;
+
+               now = time(NULL);
+               (void) localtime_r(&now, &tm);
+               aoview_mkdir(aoview_log_dir);
+               for (;;) {
+                       sprintf(base, "%04d-%02d-%02d-serial-%03d-flight-%03d.log",
+                               tm.tm_year + 1900,
+                               tm.tm_mon + 1,
+                               tm.tm_mday,
+                               aoview_log_serial,
+                               aoview_log_sequence);
+                       full = aoview_fullname(aoview_log_dir, base);
+                       r = access(full, F_OK);
+                       if (r < 0) {
+                               aoview_log_file = fopen(full, "w");
+                               if (!aoview_log_file)
+                                       aoview_log_open_failed(full);
+                               else
+                                       setlinebuf(aoview_log_file);
+                               free(full);
+                               break;
+                       }
+                       free (full);
+                       aoview_log_sequence++;
+               }
+       }
+}
+
+void
+aoview_log_printf(char *format, ...)
+{
+       va_list ap;
+
+       if (aoview_log_failed)
+               return;
+       aoview_log_start();
+       va_start(ap, format);
+       vfprintf(aoview_log_file, format, ap);
+       va_end(ap);
+}
+
+void
+aoview_log_init(GladeXML *xml)
+{
+       GConfClient     *gconf_client;
+       char            *log_dir = NULL;
+       GtkFileChooser  *log_chooser_dialog;
+       GtkWidget       *log_configure_ok;
+       GtkWidget       *log_new;
+
+       g_type_init();
+       gconf_client = gconf_client_get_default();
+       if (gconf_client)
+       {
+               log_dir = gconf_client_get_string(gconf_client,
+                                                 LOG_DIR_PATH,
+                                                 NULL);
+               g_object_unref(G_OBJECT(gconf_client));
+       }
+       if (!log_dir) {
+               aoview_log_dir = aoview_fullname(getenv("HOME"), DEFAULT_LOG);
+               aoview_log_save_conf();
+       } else {
+               aoview_log_dir = strdup(log_dir);
+       }
+
+       log_chooser_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "log_chooser_dialog"));
+       assert(log_chooser_dialog);
+       gtk_file_chooser_set_filename(log_chooser_dialog, aoview_log_dir);
+
+       log_configure_ok = glade_xml_get_widget(xml, "log_configure_ok");
+       assert(log_configure_ok);
+
+       g_signal_connect(G_OBJECT(log_configure_ok), "clicked",
+                        G_CALLBACK(aoview_log_configure),
+                        log_chooser_dialog);
+
+       log_new = glade_xml_get_widget(xml, "ao_log_new");
+       assert(log_new);
+       g_signal_connect(G_OBJECT(log_new), "activate",
+                        G_CALLBACK(aoview_log_new_item),
+                        NULL);
+
+       log_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "log_fail_dialog"));
+       assert(log_fail_dialog);
+}
index 7906e8a..9e32e63 100644 (file)
@@ -69,6 +69,10 @@ int main(int argc, char **argv)
 
        aoview_state_init(xml);
 
+       aoview_log_init(xml);
+
+       aoview_table_init(xml);
+
        gtk_main();
 
        return 0;
index 1c9b718..ba2e9df 100644 (file)
@@ -72,7 +72,8 @@ aoview_monitor_parse(char *line)
        int nword;
        struct aostate  state;
 
-       printf ("%s\n", line);
+       if (aoview_log_get_serial())
+               aoview_log_printf ("%s\n", line);
        for (nword = 0; nword < 64; nword++) {
                words[nword] = strtok_r(line, " \t\n", &saveptr);
                line = NULL;
@@ -85,6 +86,9 @@ aoview_monitor_parse(char *line)
                return;
        aoview_parse_string(state.callsign, sizeof (state.callsign), words[1]);
        aoview_parse_int(&state.serial, words[3]);
+       if (!aoview_log_get_serial())
+               aoview_log_set_serial(state.serial);
+
        aoview_parse_int(&state.rssi, words[5]);
        aoview_parse_string(state.state, sizeof (state.state), words[9]);
        aoview_parse_int(&state.tick, words[10]);
@@ -94,12 +98,15 @@ aoview_monitor_parse(char *line)
        aoview_parse_int(&state.batt, words[18]);
        aoview_parse_int(&state.drogue, words[20]);
        aoview_parse_int(&state.main, words[22]);
+       aoview_parse_int(&state.nsat, words[24]);
        if (strcmp (words[26], "unlocked") != 0 && nword >= 29) {
+               state.locked = 1;
                sscanf(words[26], "%d:%d:%d", &state.gps_time.hour, &state.gps_time.minute, &state.gps_time.second);
                aoview_parse_pos(&state.lat, words[27]);
                aoview_parse_pos(&state.lon, words[28]);
                sscanf(words[29], "%dm", &state.alt);
        } else {
+               state.locked = 0;
                state.gps_time.hour = state.gps_time.minute = state.gps_time.second = 0;
                state.lat = state.lon = 0;
                state.alt = 0;
index efd4904..046ccc9 100644 (file)
@@ -66,6 +66,9 @@ aoview_state_notify(struct aostate *state)
        int     ticks;
        double  dist;
        double  bearing;
+       double  temp;
+       double  battery;
+       double  drogue_sense, main_sense;
 
        if (!strcmp(state->state, "pad")) {
                if (npad < NUM_PAD_SAMPLES) {
@@ -90,19 +93,39 @@ aoview_state_notify(struct aostate *state)
        velocity_change = (accel + prev_accel) / 2.0;
        ticks = state->tick - prev_tick;
        velocity -= velocity_change * (ticks / 100.0);
+       temp = ((state->temp / 32767.0 * 3.3) - 0.5) / 0.01;
+       battery = (state->batt / 32767.0 * 5.0);
+       drogue_sense = (state->drogue / 32767.0 * 15.0);
+       main_sense = (state->main / 32767.0 * 15.0);
 
        prev_accel = accel;
        prev_tick = state->tick;
-       printf ("Pad altitude: %dm\n", aoview_pres_to_altitude(pad_pres));
-       printf ("AGL: %dm\n", altitude);
-       printf ("Acceleration: %gm/s²\n", accel);
-       printf ("Velocity: %gm/s\n", velocity);
-       printf ("Lat: %g\n", state->lat);
-       printf ("Lon: %g\n", state->lon);
-       printf ("GPS alt: %d\n", state->alt);
-       aoview_great_circle(pad_lat, pad_lon, state->lat, state->lon,
-                           &dist, &bearing);
-       printf ("Course: %gkm %g°\n", dist, bearing);
+       aoview_table_start();
+       aoview_table_add_row("RSSI", "%ddB", state->rssi);
+       aoview_table_add_row("Height", "%dm", altitude);
+       aoview_table_add_row("Acceleration", "%gm/s²", accel);
+       aoview_table_add_row("Velocity", "%gm/s", velocity);
+       aoview_table_add_row("Temperature", "%g°C", temp);
+       aoview_table_add_row("Battery", "%gV", battery);
+       aoview_table_add_row("Drogue", "%gV", drogue_sense);
+       aoview_table_add_row("Main", "%gV", main_sense);
+       aoview_table_add_row("Pad altitude", "%dm", aoview_pres_to_altitude(pad_pres));
+       aoview_table_add_row("Satellites", "%d", state->nsat);
+       if (state->locked) {
+               aoview_table_add_row("Lat", "%g", state->lat);
+               aoview_table_add_row("Lon", "%g", state->lon);
+               aoview_table_add_row("GPS alt", "%d", state->alt);
+               aoview_table_add_row("GPS time", "%02d:%02d:%02d",
+                                    state->gps_time.hour,
+                                    state->gps_time.minute,
+                                    state->gps_time.second);
+               aoview_great_circle(pad_lat, pad_lon, state->lat, state->lon,
+                                   &dist, &bearing);
+               aoview_table_add_row("Course", "%gkm %g°", dist, bearing);
+       } else {
+               aoview_table_add_row("GPS", "unlocked");
+       }
+       aoview_table_finish();
 }
 
 void
diff --git a/aoview/aoview_table.c b/aoview/aoview_table.c
new file mode 100644 (file)
index 0000000..90deeb9
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+static GtkTreeView     *dataview;
+static GtkListStore    *datalist;
+
+void
+aoview_table_start(void)
+{
+       datalist = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+}
+
+void
+aoview_table_add_row(char *label, char *format, ...)
+{
+       char            buf[1024];
+       va_list         ap;
+       GtkTreeIter     iter;
+
+       va_start(ap, format);
+       vsnprintf(buf, sizeof (buf), format, ap);
+       va_end(ap);
+       gtk_list_store_append(datalist, &iter);
+       gtk_list_store_set(datalist, &iter,
+                          0, label,
+                          1, buf,
+                          -1);
+}
+
+void
+aoview_table_finish(void)
+{
+       gtk_tree_view_set_model(dataview, GTK_TREE_MODEL(datalist));
+       g_object_unref(G_OBJECT(datalist));
+       gtk_tree_view_columns_autosize(dataview);
+}
+
+void
+aoview_table_init(GladeXML *xml)
+{
+       dataview = GTK_TREE_VIEW(glade_xml_get_widget(xml, "dataview"));
+       assert(dataview);
+
+       aoview_add_plain_text_column(dataview, "Field", 0, 16);
+       aoview_add_plain_text_column(dataview, "Value", 1, 32);
+}
diff --git a/aoview/aoview_util.c b/aoview/aoview_util.c
new file mode 100644 (file)
index 0000000..6ea62ac
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+char *
+aoview_fullname (char *dir, char *file)
+{
+       char    *new;
+       int     dlen = strlen (dir);
+       int     flen = strlen (file);
+       int     slen = 0;
+
+       if (dir[dlen-1] != '/')
+               slen = 1;
+       new = malloc (dlen + slen + flen + 1);
+       if (!new)
+               return 0;
+       strcpy(new, dir);
+       if (slen)
+               strcat (new, "/");
+       strcat(new, file);
+       return new;
+}
+
+char *
+aoview_basename(char *file)
+{
+       char *b;
+
+       b = strrchr(file, '/');
+       if (!b)
+               return file;
+       return b + 1;
+}
+
+int
+aoview_mkdir(char *dir)
+{
+       char    *slash;
+       char    *d;
+       char    *part;
+
+       d = dir;
+       for (;;) {
+               slash = strchr (d, '/');
+               if (!slash)
+                       slash = d + strlen(d);
+               if (!*slash)
+                       break;
+               part = strndup(dir, slash - dir);
+               if (!access(part, F_OK))
+                       if (mkdir(part, 0777) < 0)
+                               return -errno;
+               free(part);
+               d = slash + 1;
+       }
+       return 0;
+}
+
+GtkTreeViewColumn *
+aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width)
+{
+       GtkCellRenderer *renderer;
+       GtkTreeViewColumn *column;
+
+       renderer = gtk_cell_renderer_text_new ();
+       g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
+       g_object_set(renderer, "width-chars", width, NULL);
+       column = gtk_tree_view_column_new_with_attributes (title, renderer,
+                                                          "text", model_column,
+                                                          NULL);
+       gtk_tree_view_column_set_resizable (column, FALSE);
+       gtk_tree_view_append_column (view, column);
+
+       return column;
+}