#! /usr/bin/perl -w

# @@@ see browser for how to make a tag and get fixed font


##########################################################
#
# Capture and display data from Standard Counter
#
# (C) 2011 Doug Coulter, GPL v2 license -- share and enjoy,
# but if you improve it, be sure and share it back, to
# clab@swva.net, or www.coultersmithing.com/forums
#
#
# See use section below for any modules you'll need to install first
##########################################################
# It's probably obvious I'm coding this like I was a C programmer
# (because I am), and squishing fancy expressions into single liners
# would just make the inevitable changes this is going to need harder
# to figure out later -- for a C programmer (me).
#
# Anything I thought was dodgy is marked with an @@@ for searching
#
# If you're a perl purist or stylist, sorry 'bout that.  
# TMTOWTDI and, TANSTAAFL for good measure.
# lots of steal-me code here for beginning perl people
##########################################################
#
# Note, I'm using a goodly number of globals here to avoid tons of typing
# parameters into subroutines.  Most assume you'll call them in the right
# order and they're valid.  This only works for well disciplined programmers
# and not even for them if things get too big and confusing. It's not stylin,
# don't do this at home.  It does make things faster to run and to write if
# you keep it all in your head well enough. Flame me if you want.  This works.

use strict; # make perl tell me about typos and how dumb I am - built in

# Bring in a pile of library modules, this code is already written and tested by 
# someone else (mostly).  Most of these aren't standard, but have to be gotten from
# CPAN and installed via the standard way for your platform

use Gtk2 -init; # sets up GUI basics
use Glib qw(TRUE FALSE);
use Gtk2::GladeXML; # lets GUI come from a glade file
use Graphics::GnuplotIF qw(GnuplotIF); # gnuplot for plots
use File::Spec::Functions qw/:ALL/; # changes things like ./ to a real path
use File::Basename; # to get path to program for config files and logs

use Graphics::GnuplotIF qw(GnuplotIF); # gnuplot for plots

# the serial interface "requires" different modules for windows and perl, so they're not up here
# but are decided at runtime after perl tells us which opsys we have. We use "require" rather than
# "use" so we only pull in the one needed at runtime.  No system would have both.

#####################################################
################   Globals   ########################
#####################################################
my $gui; # main window
my $ssbut; # start-stop button
my $running = FALSE; # start off stopped...duh
my $makelogbut;  # button reference
my $makelog;     # boolean value
my $pbut1;       # radio 1 (no plots)
my $pbut2;	 # Geiger only
my $pbut3;	 # both
my $makeplotopt; # radio button data (0-3)
my $logplotbut;  # bool button for log plots
my $logplots;	 # bool button data
my $logwin;	 # text-edit widget reference
my $logwinbuf;	 # the text buffer reference, special GTK stuff
my $logtext;	 # finally, some actual text itself (text editor windows are complex and a PITA)
my $fonttag;	 # a gtk named tag for setting font


my $filepath;    # hold base path for local files, we'll make up the rest
my $logfilepath; # full path and filename for log file goes in this

my @xaxis;	# for gnu plots
my @gpm1;	# y axis data for geigers 1 sec intervals
my @gpm10;	# y axis data for geigers, 10 sec averages
my @npm1;	# y axis data for neutrons, 1 sec intervals
my @npm10;	# y axis data for neutrons, 10 sec averages
my $curindex;	# insertion index for next set of data

my $gplot;	# gnuplot instance pointer
my $nplot;	# one for neutrons too

my $opsys;	# for serial driver selection
my $port;	# port object reference

# debug stuff 
my $dbgctr = 0;
my $dbgtxt;
#############################################
################   Subroutines   ############
#############################################

#############################################
sub getline()
{ # get a line from std counter
 my $gotit = "";               # start clean
# while ($gotit eq "")
 { # block in loop till an end of line (\n) found
  $gotit = $port->lookfor;      # poll until data ready
  die "Aborted without match\n" unless (defined $gotit); # @@@ fixme
  my $match = $port->matchclear;# so we dont keep reading the same line over
 }
 return $gotit;
}


###########################################################
# setup serial port and return false if error, more involved than
# it should have to be, but...that's life.  Even nastier on windows
# once we get that far, count on it.  Probably going to be real hard
# to support later windows than XP or any 64 bit.  Unless you like
# running user programs as Administrator and so on.

sub setupserial()
{
 my $ttyport; # temp for port name to open
 my $portname; # name of port we'll use
 
# first find out what the opsys is
 if ($^O =~ m/linux/i) # special variable tells us opsys
 {
  require Device::SerialPort; # the correct one for linux
  $opsys = 1;  # true for linux
  $ttyport = "usb-CLAB_STD_CTR_000-if00";   # @@@ for linux only, set in PIC
  print ("\nUsing Linux serial driver\n");
 } 
 elsif ($^O =~ m/MSWin32/i)
 {
  require Win32::SerialPort; # the one for windoze
  $opsys = 0; # a big zero for win32
  $ttyport = "COM3"; # @@@ guess for now
  print ("\nUsing Windows serial driver??\n");
 }
 else
 {
  die "Unknown opsys: $^O"; # can't do much without a serial driver
 }

# now, init serial port from code defaults or config file
# @@@ this is the linux code, windows will be different?


    my $cfgfile = sprintf("%s.cfg", $ttyport);
    $cfgfile = catfile($filepath,$cfgfile); # with full path to program dir
    
    if($opsys) {$portname = sprintf("/dev/serial/by-id/%s", $ttyport);}
    else {$portname = $ttyport;}  # windows case, com? or something	 

    if ( -e $cfgfile ) {   # cfgfile exists
      $port = Device::SerialPort->start ($cfgfile) or die "Can't start $cfgfile\n";
	} else {   # cfgfile does not exist (e.g. 1st time for a tty device)
      $port = new Device::SerialPort ($portname, 1) || die "Can't open $portname: $!\n";
	}

    # some of this not needed if config file exists, this is for suspenders (and first times)
    # @@@ put this in else above, so can reset config by deleting the config on disk
      $port->databits(8);
      $port->baudrate(57600);
      $port->parity("none");
      $port->stopbits(1);
      $port->handshake("none");
      $port->stty_icanon(1);    # this is what gets us on line boundaries
      $port->are_match("\n");                       # possible end strings
      $port->lookclear;                             # empty the buffer
      $port->write_settings || undef $port; # die, sort of, if not working
      $port->save($cfgfile) || warn "Can't save $cfgfile: $!\n"; # save for next time

}
#############################################


sub resetcount()
{ # reset hardware and local data aq variables
 $port->write("n!"); #reset clock and counters
 getline(); # throw away first line after that (likely garbage)
 $port->purge_rx; # clean house of any previous junk

 @gpm1 = (); # clear all the plot data
 @gpm10 = ();
 @npm1 = ();
 @npm10 = ();
 @xaxis = (); # 
 $curindex = 0;  # stuff them starting at the start again
# clear old plots and other stuff, start new if requested
 $logwinbuf->set_text(''); # wipe any old text from the display window

}

#############################################
sub transcurrent($) # first time I've used a prototype, required by strict(!)
{ # translate current line into locals
 my $line = shift;
 my @cfields; # place to hold a split line 
 my $h; # for breaking up time
 my $m;
 my $s;
 my $cn1; # rest of the numbers on a line so we can reassemble them
 my $cn10;
 my $cnt;
 my $cg1;
 my $cg10;
 my $cgt; 
 my $csec;
 my $dtime;
# there's probably a more compact way to do the above

 @cfields = split(/\s+/,$line); # break on whitespace

 # figure out total seconds
 ($h,$m,$s) = split(/:/,$cfields[0]); # break up time and calculate total seconds
 $csec = $s + 60 * $m + 3600 * $h;
 $dtime = $cfields[0]; # save for later display

 @cfields = @cfields[2,4,6,8,10,12]; # strip off all labels via array slice
 push (@cfields," "); # @@@ loop below needs one past end
 
 $cg1 = $cfields[0]; # geiger counts in second
 $cg10 = $cfields[1]; # geiger counts in last 10 seconds
 $cgt = $cfields[2]; # total geiger counts
 
 if (defined $cfields[3]) # eg if we're putting out neutron counts too
 {
  $cn1 = $cfields[3];
  $cn10 = $cfields[4];
  $cnt = $cfields[5];
 } else
 {
  $cn1 = $cn10 = $cnt = 0; # make them defined, but zero
 }
# print ("TotalSec:$csec\n");

 $gpm1[$curindex] = $cg1;  # for gnuplot
 $gpm10[$curindex] = $cg10; # plot data
 
 # neutrons here
 $npm1[$curindex] = $cn1; # neutron plot data
 $npm10[$curindex] = $cn10;

 $xaxis[$curindex++] = $csec; # x axis for all plots

# setup a displayable line for text box
# this almost needs a perl type format setup, but I don't know how yet, so code will get complex.
# yeah, right justified numbers in fixed length fields ain't easy, is it?
 $line = $dtime; # start with time
 my $cl = length($line); # current line length
 my $dl = 20; # desired length end of first count (right justifying here)
 my $idx = 0;
 my $el = length($cfields[$idx]); # new element length
 
 while ($idx<=5)
{
 while (($cl + $el) < $dl) 
 {
  $cfields[$idx] = " " . $cfields[$idx];
  $el = length($cfields[$idx]); # new element length
 }
 $line .= $cfields[$idx]; # add it in
 $cl = length($line);    # new current length
 $idx++;                # point at next entry
 $el = length($cfields[$idx]); # update new element length
 $dl += 10;           # field width
}

 $line = join("",$dtime,$cfields[0],$cfields[1],$cfields[2],$cfields[3],$cfields[4],$cfields[5],"\n");

 # add call to text box with edited line
 addtxtline($line);

}


####################
# Make logfile path.  Assumes $filepath is already a valid path, and $logfilepath variable exists
# we'll just make a name out of the local time of day when called...should be unique
# A hassle because we can't use colons for time separator, spaces on some opsys, and slashes to make
# it look as expected, as some opsys would interpret those things as dir separators or invalid name chars
sub makelogfilepath
{
 my $tmp;
 my @time = localtime(); # get array of time data
 $tmp = join('_',$time[4]+1,$time[3],$time[5]+1900,$time[2],$time[1],$time[0]); #join the bits
 $tmp .= '.log'; # add file extension
 $logfilepath = catfile($filepath,$tmp); # put together opsys independent
 #print ("logfilepath:$logfilepath\n"); # debug
}

###########################################################
# get pathname to program directory for use in config and log files
# we need this insane dance as the program path in $0 might be ./name
# which is kind of hard to use right.  We want an absolute path where
# the script lives, and this gets it (with two modules needed!)
sub getmypath()
{
 my $path = rel2abs($0); # fix up any relative path we got
# print ("\nmunged path is: $path\n"); # debug
 $path = dirname($path); # now get just the dir without progname on it
#print ("\nfinally, path to program is: $path\n"); # debug 
 return $path;
}

####################
# create, open and insert a header line into our logfile
# assumes $logfilepath is valid when called
sub createlogfile
{
 open (LOG, "> $logfilepath") or die "Can't create $logfilepath: $!";
 print LOG "# name:$logfilepath\n"; # human readable help in file
}
####################
sub writelog # @@@ misnamed now, just closes the file, writing done as we get lines in this version
{ 
# but keep this dead code around to keep the live stuff scared.  Might want to remember
# how to do this trick someday -- it wasn't that easy to work out (arcane)

# get text buffer from edit view and write it to file, then close
  # assumes various variables are good and an open LOG filehandle
# my $start; # text iters for get text
# my $end;
# $start = $logwinbuf->get_start_iter(); # find the start
# $end = $logwinbuf->get_end_iter(); # find the end
# $logtext = $logwinbuf->get_text($start,$end,FALSE); # get the text, no hidden chars
# print LOG $logtext; # do the deed
 close (LOG) or die "Couldn't close $logfilepath: $!"; # all done with this file
}

####################

# to hook this up, in glade use gtk_main_quit as the handler in the mainwin/GtkWidget/destory-event
sub gtk_main_quit  { Gtk2->main_quit; } # fall out of main loop and exit

#########################################################################################
# we get here when you click the go/stop button
# to make this work, I had to select the "after" option for the signal in glade
# the assignment of this routine name to the toggled event was done in glade, manually
#########################################################################################
sub on_gostop_toggled
{
 if ($running)
 { # stop, write any log file
  $running = FALSE; # internal boolean flag - stop timer on next call to poll
  $ssbut->set_label('ReStart'); # relabel button
  print ("\nStopping\n"); # debug
  writelog() if $makelog; # this only checked at start time, so user can't change during a run

# I'm erasing any existing plot when we restart, 
# so you get to hit stop and still study it onscreen until you go again.

 } else # time to start up the world!
 { # setup and start things - get user's desires from GUI controls
  $running = TRUE;
  $ssbut->set_label('Stop');
  $makelog = $makelogbut->get_active() ? TRUE : FALSE; # heh, nice one-liner here
  $logplots = $logplotbut->get_active() ? TRUE : FALSE; # 3v3n better leet	
# radio button get data - @@@ let me know if there's a better way, this stinks
  if ($pbut1->get_active) {$makeplotopt = 0;} # very primitive way to get to a number
  if ($pbut2->get_active) {$makeplotopt = 1;} # and perl doesn't have a switch anyway to
  if ($pbut3->get_active) {$makeplotopt = 2;} # use this fancy number!

  undef ($gplot); # kill any old plots
  undef ($nplot); # this timing leaves them up as long as possible

  if ($makeplotopt) 
   {
    $gplot =Graphics::GnuplotIF->new(title => "Geiger Counts", raise => FALSE,
       style =>"linespoints", xlabel => 'Seconds', ylabel =>'Counts per minute');
    $gplot->gnuplot_cmd ("set grid");
    
    if ($logplots) {$gplot->gnuplot_cmd ("set log y");} # make it log
    
    $gplot->gnuplot_set_plot_titles("CPM-1","CPM-10");
   }
  if ($makeplotopt == 2) 
   {
#    print ("neutron plot create reached\n");
    $nplot = Graphics::GnuplotIF->new(title => "Neutron Counts", raise => FALSE,
       style =>"linespoints", xlabel => 'Seconds', ylabel =>'Counts per minute');
    $nplot->gnuplot_cmd ("set grid");
    $nplot->gnuplot_set_plot_titles("CPM-1","CPM-10");
    if ($logplots) {$nplot->gnuplot_cmd ("set log y");} # make it log
   }

  makelogfilepath(); # create a path and name in current program directory
  createlogfile() if $makelog; # start up a log if requested

  print ("\nReset and startup. Makelog:$makelog Makeplotopt:$makeplotopt,log plots:$logplots\n"); # debug
  resetcount(); # clear plot buffers, reset hardware, clear serial data
#  open (DATA, "< $filepath/test.dat") or die "couldn't open fake datafile: $!"; # debug
  

  Glib::Timeout->add(250, \&poll); # start the calls to the poll subroutine
 } # end if running else "start case"

} # end on_gostop_toggled

####################

# sub to add a line to our text view, then scroll it to the end
# found this trick here: http://www.gtkforums.com/viewtopic.php?t=1307
# and  translated it from C to perl...simpler in perl, actually
sub addtxtline
{ # text edit boxes are a PITA no matter the opsys or GUI toolset, it seems
  # it's not like you could do better -- the full editor is a complex thing
 my $line = shift; # whatever you handed us to insert at end
 my $itere; # a "thing" that marks a place in a textedit buffer
 my $iterb; # same for an iter that points to start
 $itere = $logwinbuf->get_end_iter(); # find the end
 $logwinbuf->insert($itere,$line); # insert at end
 $logwin->scroll_to_iter($itere,0,FALSE,0,0); # scroll to make it visible
 # stuff to get fixed font.  Evidently have to do this for each new line 
 # might only need iters for that one line, but heck, that's harder than this is
 $iterb = $logwinbuf->get_start_iter(); # need to refresh both after buffer mod 
 $itere = $logwinbuf->get_end_iter(); # find the end
 $logwinbuf->apply_tag_by_name ("font", $iterb, $itere); # make all fixed font
}

#################### this one's where the action is during a run
sub poll
{ # we'll have GTK call this on quarter seconds to look for a line of input, 
  # do stuff about that, or return if nothing to do
 return FALSE unless $running; # this also stops the timer hits

#debug here to create some fake data to test log and plot with
# read fake datafile and insert line
#  if ($dbgtxt = <DATA>)
#  {
#   unless ($dbgtxt =~ m/#/) 
#   {
#    transcurrent($dbgtxt); # translate input into arrays of numbers, one line
#    if ($gplot) {$gplot->gnuplot_plot_xy( \@xaxis, \@gpm1, \@gpm10);} # rewrite plot
#    if ($nplot) {$nplot->gnuplot_plot_xy( \@xaxis, \@npm1, \@npm10);} # rewrite plot
#   } # skip comments
#  } else {return FALSE;} # stop the music at EOF

# try to get a line from serial port in real thing here

 my $dataline = getline(); # attempt to get a line from hardware port (or later, a file)
 unless ($dataline eq "")
 {
  if ($makelog) {print LOG "$dataline\n";} # put the raw line in the logfile if requested
  transcurrent ($dataline); # translate current line for plotting and logging, add in text view
  if ($gplot) {$gplot->gnuplot_plot_xy( \@xaxis, \@gpm1, \@gpm10);} # rewrite plot
  if ($nplot) {$nplot->gnuplot_plot_xy( \@xaxis, \@npm1, \@npm10);} # rewrite plot
 }

 return TRUE; # keep timer running
}

#######################################################################################
################  Main program   ######################################################
#######################################################################################
my $guidata; # test to see if we can pack the xml internally and get it back

#print ("\nprogram running\n"); # debug
$filepath = getmypath(); # find out where we are despite opsys munging - base dir for all other files
#print "path:$filepath\n"; # debug

undef $/; # remove normal record terminator
$guidata = <main::DATA>;
#print $guidata; # debug
$/ = "\n"; # put record terminator back

#$gui = Gtk2::GladeXML->new(catfile($filepath,'StdC.glade')); # uses right type of dir separators for opsys
#@@@ note there is also a new_from_buffer($buffer) that can be used to do this from a string
# once things are stable, this means we can get rid of the glade file and put it here under __END__ instead
# or some other way to put all that XML in here
$gui = Gtk2::GladeXML->new_from_buffer($guidata); # and here it is, cool.  Saved old code for later...
# this is also noticeably quicker to come up

$ssbut = $gui->get_widget('gostop'); # pointer to start button?
$makelogbut = $gui->get_widget('logfileb'); # pointer to make logfile check button
$pbut1 = $gui->get_widget('radiobutton1'); # pointers to radiobuttons, a little primitive here
$pbut2 = $gui->get_widget('radiobutton2'); # pointers to radiobuttons, a little primitive here
$pbut2->set_active(TRUE);
$pbut3 = $gui->get_widget('radiobutton3'); # pointers to radiobuttons, a little primitive here
$logplotbut = $gui->get_widget('logplotb'); # button for log-y plotting
$logplotbut->set_active(TRUE);
$logwin = $gui->get_widget('logwin'); # pointer to text display window
$logwinbuf = $logwin->get_buffer(); # later use $logwinbuf->set_text($some_text);
$logwinbuf->create_tag("font",'font'=>'monospace'); # used to set font on text edit window
$gui->signal_autoconnect_from_package('main'); # hooks subs to signals if things are named right

setupserial();	# set up the serial port (fails if USB and not plugged in on linux)

# Enter the event loop
Gtk2->main;

# any cleanup here after user hits quit

__END__  # we've put the glade xml file data here and read it from within - so if you change the glade...
<?xml version="1.0"?>
<glade-interface>
  <!-- interface-requires gtk+ 2.16 -->
  <!-- interface-naming-policy project-wide -->
  <widget class="GtkWindow" id="mainwin">
    <property name="visible">True</property>
    <property name="tooltip" translatable="yes">Standard geiger counter for measuring activations.</property>
    <property name="title" translatable="yes">Standard Geiger Counter</property>
    <property name="default_width">512</property>
    <property name="default_height">768</property>
    <signal name="destroy" handler="gtk_main_quit"/>
    <signal name="destroy_event" handler="gtk_main_quit"/>
    <child>
      <widget class="GtkVPaned" id="vpaned1">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <child>
          <widget class="GtkLabel" id="label1">
            <property name="visible">True</property>
            <property name="xalign">0</property>
            <property name="label" translatable="yes">     Parameters            Runtime                 GPM1         GPM10               TG           NPM1        NPM10                TN	</property>
          </widget>
          <packing>
            <property name="resize">False</property>
            <property name="shrink">True</property>
          </packing>
        </child>
        <child>
          <widget class="GtkHPaned" id="hpaned1">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <child>
              <widget class="GtkVButtonBox" id="vbuttonbox1">
                <property name="visible">True</property>
                <child>
                  <widget class="GtkCheckButton" id="logfileb">
                    <property name="label" translatable="yes">Write Log File?</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">False</property>
                    <property name="draw_indicator">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">0</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator1">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">1</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkLabel" id="label2">
                    <property name="visible">True</property>
                    <property name="label" translatable="yes">Plot Selections</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">2</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkRadioButton" id="radiobutton1">
                    <property name="label" translatable="yes">No Plots</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">False</property>
                    <property name="active">True</property>
                    <property name="draw_indicator">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">3</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkRadioButton" id="radiobutton2">
                    <property name="label" translatable="yes">Geiger Only</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">False</property>
                    <property name="draw_indicator">True</property>
                    <property name="group">radiobutton1</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">4</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkRadioButton" id="radiobutton3">
                    <property name="label" translatable="yes">Geiger + Aux</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">False</property>
                    <property name="draw_indicator">True</property>
                    <property name="group">radiobutton1</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">5</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator16">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">6</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkCheckButton" id="logplotb">
                    <property name="label" translatable="yes">Log Plots?</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">False</property>
                    <property name="draw_indicator">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">7</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator3">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">8</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator4">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">9</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator5">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">10</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator6">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">11</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator7">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">12</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator8">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">13</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator9">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">14</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator10">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">15</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator11">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">16</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator12">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">17</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator13">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">18</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator14">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">19</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkHSeparator" id="hseparator15">
                    <property name="visible">True</property>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">20</property>
                  </packing>
                </child>
                <child>
                  <widget class="GtkToggleButton" id="gostop">
                    <property name="label" translatable="yes">Go</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="draw_indicator">True</property>
                    <signal name="toggled" handler="on_gostop_toggled" after="yes"/>
                  </widget>
                  <packing>
                    <property name="expand">False</property>
                    <property name="fill">False</property>
                    <property name="position">21</property>
                  </packing>
                </child>
              </widget>
              <packing>
                <property name="resize">False</property>
                <property name="shrink">True</property>
              </packing>
            </child>
            <child>
              <widget class="GtkScrolledWindow" id="txtscroll">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hscrollbar_policy">automatic</property>
                <child>
                  <widget class="GtkTextView" id="logwin">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="editable">False</property>
                    <property name="left_margin">10</property>
                    <property name="accepts_tab">False</property>
                  </widget>
                </child>
              </widget>
              <packing>
                <property name="resize">True</property>
                <property name="shrink">True</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="resize">True</property>
            <property name="shrink">True</property>
          </packing>
        </child>
      </widget>
    </child>
  </widget>
</glade-interface>
