+ my $t = shift || time;
+ return sprintf "%02d:%02d:%02d", (gmtime($t))[2,1,0];
+}
+
+sub stim
+{
+ my $t = shift || time;
+ return sprintf "%02d:%02d", (gmtime($t))[2,1];
+}
+
+# get a zulu time in cluster format (2300Z)
+sub ztime
+{
+ my $t = shift;
+ $t = defined $t ? $t : time;
+ my $dst = shift;
+ my ($sec,$min,$hour) = $dst ? localtime($t): gmtime($t);
+ my $buf = sprintf "%02d%02d%s", $hour, $min, ($dst) ? '' : 'Z';
+ return $buf;
+}
+
+# get a cluster format date (23-Jun-1998)
+sub cldate
+{
+ my $t = shift;
+ $t = defined $t ? $t : time;
+ my $dst = shift;
+ my ($sec,$min,$hour,$mday,$mon,$year) = $dst ? localtime($t) : gmtime($t);
+ $year += 1900;
+ my $buf = sprintf "%2d-%s-%04d", $mday, $month[$mon], $year;
+ return $buf;
+}
+
+# return a cluster style date time
+sub cldatetime
+{
+ my $t = shift;
+ my $dst = shift;
+ my $date = cldate($t, $dst);
+ my $time = ztime($t, $dst);
+ return "$date $time";
+}
+
+sub read_user_data
+{
+ my $u;
+
+ if (-e $userfn) {
+ my $fh = new IO::File $userfn;
+ my $s = undef;
+ if ($fh) {
+ local $/ = undef;
+ $s = <$fh>;
+ $fh->close;
+ }
+ eval "\$u = $s";
+ }
+ unless ($u) {
+ print "$userfn missing or unreadable, starting afresh!\n";
+
+ $u = {
+ clusters => {
+ 'LOCAL' => {host => '127.0.0.1', port => 7300},
+ 'GB7DJK' => {host => 'gb7djk.dxcluster.net', port => 7300},
+ 'WR3D' => {host => 'wr3d.dxcluster.net', port => 7300},
+ 'GB7BAA' => {host => 'gb7baa.dxcluster.net', port => 7300},
+ },
+ node => 'LOCAL',
+ call => $main::myalias,
+ };
+ write_user_data($u);
+ }
+ return $u;
+}
+
+sub write_user_data
+{
+ my $u = shift;
+
+ my $fh = new IO::File ">$userfn";
+ if ($fh) {
+ my $dd = new Data::Dumper([ $u ]);
+ $dd->Indent(1);
+ $dd->Terse(1);
+ $dd->Quotekeys(0);
+ $fh->print($dd->Dumpxs);
+ $fh->close;
+ return 1;
+ }
+ return 0;
+}
+
+sub def_menu_callback
+{
+
+}
+
+sub gtk_create_main_screen
+{
+ $main = new Gtk2::Window('toplevel');
+ my $scr = $main->get_screen;
+ $scr_width = int ($scr->get_width > 1280 ? 1280 : $scr->get_width) * 0.99;
+ $scr_height = int $scr->get_height * 0.5;
+ $main->set_default_size($scr_width, $scr_height);
+ $main->signal_connect('delete_event', sub { Gtk2->main_quit; });
+
+ # the main vbox
+ my $vbox = new Gtk2::VBox(0, 1);
+ $main->add($vbox);
+
+ my $menutree = [
+ _File => {
+ item_type => '<Branch>',
+ children => [
+ _Quit => {
+ callback => sub { Gtk2->main_quit; },
+ callback_action => 1,
+ accelerator => '<ctrl>Q',
+ }
+ ],
+ },
+
+ _Help => {
+ item_type => '<Branch>',
+ children => [
+ _About => {
+ callback_action => 9,
+ },
+ ],
+ },
+
+ ];
+
+ my $menu = Gtk2::SimpleMenu->new(menu_tree => $menutree, default_callback => \&def_menu_callback, user_data => $user);
+ $vbox->pack_start($menu->{widget}, 0, 1, 0);
+
+
+ # a paned hbox is packed as the bottom of the vbox
+ my $bhpane = Gtk2::HPaned->new;
+ $vbox->pack_end($bhpane, 1, 1, 0);
+
+ # now create the lh and rh panes
+ my $lhvpane = Gtk2::VPaned->new;
+ my $rhvpane = Gtk2::VPaned->new;
+ $bhpane->pack1($lhvpane, 1, 0);
+ $bhpane->pack2($rhvpane, 1, 0);
+
+ #
+ # LEFT HAND SIDE
+ #
+ # The announce list
+ $ann = Screen::List->new(fields =>[
+ RxTime => 'tt',
+ From => 'tt',
+ To => 'tt',
+ Announcement => 'ttlesslong',
+ ],
+ hint => 1,
+ frame => 'Announcements',
+ size => [$scr_width * 0.45, $scr_height * 0.33],
+ );
+
+ $lhvpane->pack1($ann->widget, 1, 0);
+
+ # The command list
+ my $lhvbox = Gtk2::VBox->new(0, 1);
+ $cmd = Screen::List->new(fields => [
+ RxTime => 'tt',
+ Information => 'ttlong',
+ ],
+ size => [$scr_width * 0.45, $scr_height * 0.66],
+ );
+ $lhvbox->pack_start($cmd->widget, 1, 1, 0);
+
+
+ # callsign and current date and time
+ my $hbox = new Gtk2::HBox;
+ my $calllabel = new Gtk2::Label($call);
+ my $date = new Gtk2::Label(cldatetime(time));
+ $date->{tick} = Glib::Timeout->add(1000, \&updatetime, $date);
+ $hbox->pack_start( $calllabel, 0, 1, 0 );
+ $hbox->pack_end($date, 0, 1, 0);
+ $lhvbox->pack_start($hbox, 0, 1, 0);
+ $lhvbox->pack_start(Gtk2::HSeparator->new, 0, 1, 0);
+
+ # the bottom handler
+ $bot = new Gtk2::Entry;
+ $bot->set_editable(1);
+ $bot->signal_connect('activate', \&bothandler);
+ $bot->can_default(1);
+ $lhvbox->pack_end($bot, 0, 1, 0);
+ $lhvpane->pack2($lhvbox, 1, 0);
+ $bot->grab_default;
+
+ #
+ # RIGHT HAND SIDE
+ #
+
+ # DX window
+ $dx = Screen::List->new(fields => [
+ 'RxTime' => 'tt',
+ 'QRG' => 'qrg',
+ 'DX Call' => 'tt',
+ 'Grid' => 'tt',
+ 'Remarks' => 'ttshort',
+ 'By' => 'tt',
+ 'Grid' => 'tt',
+ 'TxTime' => 'tt',
+ ],
+ policy => [qw(never automatic)],
+ hint => 1,
+ frame => "DX Spots",
+ maxsize => 500,
+ size => [$scr_width * 0.45, $scr_height * 0.45],
+ );
+ $rhvpane->pack1($dx->widget, 1, 0);
+
+ # The wwv list
+ my $rhvbox = Gtk2::VBox->new(0, 1);
+ $wwv = Screen::List->new( fields =>[
+ RxTime => 'tt',
+ From => 'tt',
+ SFI => 'int',
+ A => 'int',
+ K => 'int',
+ Remarks => 'ttshort',
+ Hour => 'tt'
+ ],
+ hint => 1,
+ policy => ['never', 'automatic'],
+ frame => 'WWV Data',
+ );
+ $rhvbox->pack_start($wwv->widget, 1, 1, 0);
+
+ # The wcy list
+ $wcy = Screen::List->new(fields => [
+ RxTime => 'tt',
+ From => 'tt',
+ K => 'int',
+ ExpK => 'int',
+ A => 'int',
+ R => 'int',
+ SFI => 'int',
+ SA => 'tt',
+ GMF => 'tt',
+ Aurora => 'tt',
+ Hour => 'tt'
+ ],
+ hint => 1,
+ policy => ['never', 'automatic'],
+ frame => 'WCY Data',
+ );
+
+ $rhvbox->pack_start($wcy->widget, 1, 1, 0);
+ $rhvbox->set_size_request($scr_width * 0.45, $scr_height * 0.33);
+ $rhvpane->pack2($rhvbox, 1, 0);