#!/bin/sh
# the next line restarts using wish \
exec wish "$0" "$@"

###############################################################
# TkNet - Charlie KEMPSON - Version 1.0
###############################################################

###############################################################
#    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.
#
#    For a copy of the GNU General Public License, write to the 
#    Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
#    MA 02139, USA.
###############################################################

###############################################################
# Set include path
set GLOBAL_INCLUDE "/usr/local/lib/TkNet"

###############################################################
# Set global variables

# Initialise constants
set FIXED_FONT_SMALL   -*-courier-medium-r-*-*-*-*-*-*-*-*-*-*
set FIXED_FONT          -*-courier-medium-r-*-*-14-*-*-*-*-*-*-*
set FONT_NORMAL         -adobe-helvetica-medium-r-*-*-14-*-*-*-*-*-*-*
set FONT_ITALIC         -adobe-helvetica-medium-o-*-*-14-*-*-*-*-*-*-*
set FONT_BOLD           -adobe-helvetica-bold-r-*-*-14-*-*-*-*-*-*-*
set BUTTON_COLOUR       Grey65
set TEXT_COLOUR         Grey85
set DEFAULT_COLOUR      Grey75
set HELP_COLOUR         HotPink4
set LAUNCHER_COLOUR     Grey60
set LAUNCHER_FONT       -adobe-helvetica-medium-r-*-*-*-*-*-*-*-*-*-*
set LAUNCHER_ITEM_LENGTH 20
set SCRIPT_ITEM_LENGTH  20
set SCRIPT_LABEL_LENGTH  15
set RED                 Red
set GREEN               ForestGreen
set VERSION             1.0
set DATE                (12/1/95)
set LOWER_BORDER        3
set RIDGE_BORDER        2
set DEFAULT_PADDING     5
set BITMAP_HEIGHT       60
set BITMAP_WIDTH        40
set OPTION_TEXT_LENGTH  30
set OPTION_LABEL_LENGTH 30
set TKNET_DEFAULT_USER  "root"
set TKNET_DEFAULT_USER_HOME  "/tmp"
set TKNET_HELP_FILE     [join [list $GLOBAL_INCLUDE / tknet.help] ""]

# Initialise Flags
set gb_connection_down  1
set gb_connection_down_prev  1
set gb_debug_on         0
set g_wait_time         0
set g_connect_process   0
set gb_waiting_for_connection 0
set gb_mail_window_up   0
set gb_delivering_mail  0
set gs_incoming_mail    ""
set gs_outgoing_mail    ""
set g_spool_file_size   0
set g_log_file_size     0
set g_option_number     0
set g_separator_number  0
set g_setup_changed     0
set g_launcher_changed  0
set g_current_launcher  0
set g_setup_file        /usr/local/lib/TkNet/tknet.setup
set g_launcher_number   0
set gi_clear_message    0
set g_script_number     0
set g_script_changed    0
set gi_default_script   0
set g_current_script    0
set g_current_connect   ""
set gt_last_stat_time   0
set g_previous_stat     0
set g_throughput        0
set g_log_messages      0
set gt_time_connected   0
set gt_saved_time       "000/00:00:00"

# Set Tk/tcl global variables
set tk_strictMotif      1
set tcl_precision       3

# Get the user's environment
if [catch {set env(HOME)} TKNET_USER_HOME] {
   set TKNET_USER_HOME $TKNET_DEFAULT_USER_HOME
}
if [catch {set env(USER)} TKNET_USER] {
   set TKNET_USER $TKNET_DEFAULT_USER
}

# Flags to be saved
set TKNET_GLOBAL_OPTIONS_FILE [join [list $GLOBAL_INCLUDE \
   / tknet.preferences] ""]
set TKNET_LOCAL_OPTIONS_FILE [join [list $TKNET_USER_HOME \
   / .tknet_preferences] ""]
set gls_options_list [list gb_ping_connection gb_auto_reconnect \
                          gb_reset_clock_on_connect gb_beep_on_connect \
                          gb_beep_on_new_mail gb_show_microhelp \
                          gb_show_launcher_at_startup gb_confirm_quit \
                          gb_launcher_window gi_launcher_button_style \
                          gb_show_time gb_show_buttons gb_show_messages\
                          gb_save_positions gb_check_for_mail \
                          gb_startup_iconified gb_launcher_vertical \
                          gb_show_stats]
set gls_geometry_list [list TKNET_GEOMETRY \
                          TKNET_LOG_GEOMETRY TKNET_SETUP_GEOMETRY \
                          TKNET_LAUNCHER_SETUP_GEOMETRY \
                          TKNET_MAIL_GEOMETRY TKNET_LAUNCHER_GEOMETRY \
                          TKNET_HELP_GEOMETRY TKNET_SCRIPT_SETUP_GEOMETRY]
set gb_ping_connection  1
set gb_auto_reconnect   0
set gb_reset_clock_on_connect 0
set gb_beep_on_connect  1
set gb_check_for_mail   1
set gb_beep_on_new_mail 0
set gb_show_microhelp   0
set gb_show_launcher_at_startup 1
set gb_launcher_window  0
#1=labels,2=bitmaps,3=both
set gi_launcher_button_style 1 
set gb_confirm_quit     1
set gb_show_time        1
set gb_show_buttons     1
set gb_show_messages    1
set gb_save_positions   0
set gb_startup_iconified 0
set gb_launcher_vertical 0
set gb_show_stats       0

# Window position information
set TKNET_GEOMETRY      "+15+15"
set TKNET_LOG_GEOMETRY  "+500+51"
set TKNET_LAUNCHER_SETUP_GEOMETRY "+408+51"
set TKNET_LAUNCHER_GEOMETRY "+100+100"
set TKNET_SETUP_GEOMETRY "+408+51"
set TKNET_MAIL_GEOMETRY "+21+262"
set TKNET_HELP_GEOMETRY "+200+200"
set TKNET_SCRIPT_SETUP_GEOMETRY "+408+51"

# Initialise configuration
set TKNET_GLOBAL_SETUP_FILE [join [list $GLOBAL_INCLUDE \
   / tknet.setup] ""]
set TKNET_LOCAL_SETUP_FILE [join [list $TKNET_USER_HOME \
   / .tknet_setup] ""]
set gls_setup_list [list g_ping_command g_runq_command \
                         g_mailtool g_network_interface \
                         g_mail_refresh_freq g_check_remote_mail \
                         g_check_local_mail g_clock_refresh_freq \
                         g_log_file g_network_check_freq \
                         g_max_wait_time gs_remote_mail_indicator \
                         gs_local_mail_indicator gs_spool_dir \
                         g_message_display_time g_connected_pixmap \
                         g_disconnected_pixmap g_link_stats \
                         g_idle_disconnect_time \
                         g_biff_check]

set gs_ping_command_text "Keepalive connection"
set g_ping_command      "ping -c 1 nether.demon.co.uk"

set gs_runq_command_text "Run the mail queue"
set g_runq_command      "su1 sendmail -q"

set gs_mailtool_text    "Start mail reader"
set g_mailtool          "exmh"

set gs_network_interface_text "Network interface" 
set g_network_interface  "sl0"

set gs_mail_refresh_freq_text "Refresh mail queues (secs)"
set g_mail_refresh_freq 20

set gs_check_remote_mail_text "Remote mail queue"
set g_check_remote_mail "finger siren@post.demon.co.uk"

set gs_check_local_mail_text "Local mail queue"
set g_check_local_mail  "sendmail -bp"

set gs_clock_refresh_freq_text "Refresh clock every (secs)"
set g_clock_refresh_freq 10

set gs_log_file_text    "TkNet log file"
set g_log_file          /tmp/tknetlog.log

set gs_network_check_freq_text "Check connection every (secs)"
set g_network_check_freq 5

set gs_max_wait_time_text "Timeout after (secs)"
set g_max_wait_time     60

set gs_remote_mail_indicator_text "Remote mail indicator"
set gs_remote_mail_indicator "From"

set gs_local_mail_indicator_text "Local mail indicator"
set gs_local_mail_indicator "Size"

set gs_spool_dir_text   "Spool directory"
set gs_spool_dir        "/usr/spool/mail"

set g_message_display_time_text   "Display messages for (secs)"
set g_message_display_time 7

set g_connected_pixmap_text   "Pixmap when connected"
set g_connected_pixmap "/usr/include/X11/pixmaps/tknetup.xpm"

set g_disconnected_pixmap_text   "Pixmap when disconnected"
set g_disconnected_pixmap "/usr/include/X11/pixmaps/tknetdn.xpm"

set g_link_stats_text  "Link statistics every (secs)"
set g_link_stats 30

set g_idle_disconnect_time_text "Disconnect after idle (mins)"
set g_idle_disconnect_time 0

set g_biff_check_text "BIFF check every (secs)"
set g_biff_check 10

# Initialise configuration
set TKNET_GLOBAL_LAUNCHER_FILE [join [list $GLOBAL_INCLUDE \
   / tknet.launcher] ""]
set TKNET_LOCAL_LAUNCHER_FILE [join [list $TKNET_USER_HOME \
   / .tknet_launcher] ""]

set TKNET_GLOBAL_SCRIPT_FILE [join [list $GLOBAL_INCLUDE \
   / tknet.scripts] ""]
set TKNET_LOCAL_SCRIPT_FILE [join [list $TKNET_USER_HOME\
   / .tknet_scripts] ""]

# Initialise microhelp strings
set gs_help_connect     "Press to connect to the network"
set gs_help_disconnect  "Press to disconnect from the network"
set gs_help_quit        "Press to disconnect from the network and quit TkNet"
set gs_help_mail        "Press to invoke the mail window"
set gs_help_log         "Press to invoke the log window"
set gs_help_deliver     "Press to deliver/retrieve mail and disconnect"
set gs_help_time        "Length of time connected for (DDD/HH:MM:SS)"
set gs_help_through     "Average link throughput (while connected)"
set gs_help_status      "Network connection status (click to toggle)"
set gs_help_popup       "Click the right hand mouse button for menu"

###############################################################
# Set application defaults

# Fonts
option add *font $FONT_NORMAL startupFile

# Colours
option add *background  $DEFAULT_COLOUR startupFile
option add *Text.background  $TEXT_COLOUR startupFile

###############################################################
# Set a message into the main window message line
proc Debug {text} {

   # Globals
   global g_log_file gb_debug_on

   if {$gb_debug_on == 1} {

      # Log the message to file (if not help)
      set log_file_id [ open $g_log_file a+ ]

      # Write the message to file
      puts -nonewline $log_file_id "DEBUG : "
      puts $log_file_id $text
      flush $log_file_id

      # Close log file
      close $log_file_id
   }
}

###############################################################
# The procedure to read the setup files
# Argument must be 0=Both 1=Global or 2=Local 
proc Source_Setup { file } {

   # Globals
   global TKNET_GLOBAL_SETUP_FILE \
      TKNET_LOCAL_SETUP_FILE g_ping_command \
      g_runq_command g_mailtool g_network_interface \
      g_mail_refresh_freq g_check_remote_mail \
      g_check_local_mail g_clock_refresh_freq \
      g_log_file g_network_check_freq g_max_wait_time \
      gs_remote_mail_indicator gs_local_mail_indicator \
      gs_spool_dir g_message_display_time \
      g_connected_pixmap g_disconnected_pixmap \
      g_link_stats g_idle_disconnect_time g_biff_check
      
# Read the global setup file
   if {$file != 2 && [file exists $TKNET_GLOBAL_SETUP_FILE]} {
      source $TKNET_GLOBAL_SETUP_FILE
   }
   
   # Read the user's own config file if it exists
   if {$file != 1 && [file exists $TKNET_LOCAL_SETUP_FILE]} {
      # Override with users own
      source $TKNET_LOCAL_SETUP_FILE
   }

   # Note that if neither exist the defaults are
   # taken from the internal settings corresponding
   # to my home settings.
}

###############################################################
# The procedure to read the options files
# Argument must be 0=Both 1=Global or 2=Local 
proc Source_Options { file } {

   # Globals
   global TKNET_GLOBAL_OPTIONS_FILE \
      TKNET_LOCAL_OPTIONS_FILE \
      gb_ping_connection gb_auto_reconnect \
      gb_reset_clock_on_connect gb_beep_on_connect \
      gb_beep_on_new_mail gb_show_microhelp \
      gb_show_launcher_at_startup gb_confirm_quit \
      gb_launcher_window gi_launcher_button_style \
      gb_show_time gb_show_buttons gb_save_positions \
      TKNET_GEOMETRY TKNET_LOG_GEOMETRY \
      TKNET_SETUP_GEOMETRY TKNET_LAUNCHER_SETUP_GEOMETRY \
      TKNET_MAIL_GEOMETRY TKNET_LAUNCHER_GEOMETRY \
      TKNET_HELP_GEOMETRY gb_check_for_mail \
      gb_show_messages gb_startup_iconified \
      TKNET_SCRIPT_SETUP_GEOMETRY gb_launcher_vertical \
      gb_show_stats

   # Read the global options file
   if {$file != 2 && [file exists $TKNET_GLOBAL_OPTIONS_FILE]} {
      source $TKNET_GLOBAL_OPTIONS_FILE
   }
   
   # Read the user's own config file if it exists
   if {$file != 1 && [file exists $TKNET_LOCAL_OPTIONS_FILE]} {
      # Override with users own
      source $TKNET_LOCAL_OPTIONS_FILE
   }

   # Note that if neither exist the defaults are
   # taken from the internal settings corresponding
   # to my home settings.
}

###############################################################
# The procedure to read the launcher setup files
# Argument must be 0=Both 1=Global or 2=Local 
proc Source_Launcher_Setup { file } {

   # Globals
   global TKNET_GLOBAL_LAUNCHER_FILE \
      TKNET_LOCAL_LAUNCHER_FILE

   # Read the global setup file
   if {$file != 2 && [file exists $TKNET_GLOBAL_LAUNCHER_FILE]} {
      source $TKNET_GLOBAL_LAUNCHER_FILE
   }
   
   # Read the user's own config file if it exists
   if {$file != 1 && [file exists $TKNET_LOCAL_LAUNCHER_FILE]} {
      # Override with users own
      source $TKNET_LOCAL_LAUNCHER_FILE
   }

   # Note that if neither exist the defaults are
   # taken from the internal settings corresponding
   # to my home settings.
}

###############################################################
# The procedure to read the Script setup files
# Argument must be 0=Both 1=Global or 2=Local 
proc Source_Script_Setup { file } {

   # Globals
   global TKNET_GLOBAL_SCRIPT_FILE \
      TKNET_LOCAL_SCRIPT_FILE g_current_script \
      gi_default_script

   # Read the global setup file
   if {$file != 2 && [file exists $TKNET_GLOBAL_SCRIPT_FILE]} {
      source $TKNET_GLOBAL_SCRIPT_FILE
   }
   
   # Read the user's own config file if it exists
   if {$file != 1 && [file exists $TKNET_LOCAL_SCRIPT_FILE]} {
      # Override with users own
      source $TKNET_LOCAL_SCRIPT_FILE
   }

   # And set the current default script
   set g_current_script $gi_default_script

   # Note that if neither exist the defaults are
   # taken from the internal settings corresponding
   # to my home settings.
}

###############################################################
# The procedure for connecting
proc Toggle_Connection { } {
   Debug "Toggle_Connection"

   # Globals
   global gb_connection_down gb_waiting_for_connection

   # If a connection attempt is already being made,
   # ask the user if they wish to terminate the 
   # current attempt.  Otherwise, connect/disconnect
   # on the basis of the flag.

   if {$gb_waiting_for_connection ==1} {
      if ![Question_Dialog . \
         "You are in the process of connecting.\n\
Terminate this connection attempt?" "Yes" "No"] {
         # Terminate connection attempt
         Net_Disconnect
      }
   } elseif {$gb_connection_down == 1} {
      # Connect
      Net_Connect
   } else {
      if ![Question_Dialog . \
         "Terminate connection?" "Yes" "No"] {
         # Terminate connection attempt
         Net_Disconnect
      }
   }
}

###############################################################
# The procedure for connecting
proc Net_Connect { } {
   Debug "Net_Connect"

   # Globals
   global gb_connection_down g_connection_status \
      g_connection_time g_log_file g_connect_process GREEN RED \
      gb_waiting_for_connection g_wait_time g_current_script

   # Set an hourglass
   WatchCursor

   # Start the connection process in the background
   if {$gb_connection_down == 1} {

      # If a connection process is already running, don't
      # start another one!
      set process_running [ Is_Process_Running $g_connect_process ]
      if {$process_running == 1} {
         Set_Message "Already Connecting ..." $RED
      } else {
         # Start connection process

         # Get the script label and command string
         global "g_script_label_$g_current_script" \
            "g_script_script_$g_current_script"
         set label [eval subst {\$g_script_label_$g_current_script}]
         set script [eval subst {\$g_script_script_$g_current_script}]

         Set_Message "Connecting using $label" $GREEN
         set g_connect_process [ eval exec $script >>& \
            $g_log_file & ]

         # Set the flag
         set gb_waiting_for_connection 1
         set g_wait_time 0
      }
   } else {
      Set_Message "Already Connected!" $RED
   }

   # Reset the hourglass
   NormalCursor
}

###############################################################
# The procedure for disconnecting
proc Net_Disconnect { } {
   Debug "Net_Disconnect"

   # Globals
   global g_connection_time g_log_file \
      g_disconnect_process gb_delivering_mail GREEN RED \
      gb_waiting_for_connection g_current_script

   # Set an hourglass
   WatchCursor

   # If in the middle of an auto-retrieve mail, stop it
   if {$gb_delivering_mail == 1} {

      # reset the flag
      set gb_delivering_mail 0
   }

   # Reset the waiting for connection flag
   set gb_waiting_for_connection 0

   # Start the disconnection process in the background
   # Get the script label and command string
   global "g_script_label_$g_current_script" \
      "g_script_disconnect_$g_current_script"
   set label [eval subst {\$g_script_label_$g_current_script}]
   set script [eval subst {\$g_script_disconnect_$g_current_script}]

   Set_Message "Attempting disconnection from $label" $GREEN
   set g_disconnect_process [ eval exec $script >>& \
      $g_log_file & ]

   # Reset the hourglass
   NormalCursor
}

###############################################################
# The procedure for delivering mail
proc Deliver_Mail { } {
   Debug "Deliver_Mail"

   # Globals
   global GREEN RED gb_delivering_mail

   # Set an hourglass
   WatchCursor
   Set_Message "Fetching / delivering mail ..." $GREEN

   # Start the connection process in the background
   Net_Connect

   # Set the disconnect when mail delivered flag.   This is checked by the
   # Background_Mail_Check routine - when there is no mail left to deliver
   # a disconnect is performed and the mail window is closed.
   set gb_delivering_mail 1

   # Open the mail window
   Show_Mail

   # Reset the hourglass
   NormalCursor
}

###############################################################
# The procedure for updating the clock
proc Update_Clock {} {
   Debug "Update_Clock"

   # Globals
   global gb_connection_down g_connection_time \
      gb_ping_connection gb_beep_on_connect g_ping_command \
      g_clock_refresh_freq g_connection_status GREEN RED \
      g_log_file gb_auto_reconnect g_disconnected_pixmap \
      gt_time_connected gt_saved_time

   # Only update clock if connected
   Set_Connection_Flag

   if {$gb_connection_down == 0} {
      set Time [Get_Current_Time]

      # Set into the text widget
      set connected_for [AddSub_Time $gt_time_connected \
         $Time -1 2]
      set connected_for [AddSub_Time $connected_for \
         $gt_saved_time 1 2]
      $g_connection_time configure -text $connected_for

      # Rerun after N secs
      if {$g_clock_refresh_freq > 0} {
         set run_after [ expr $g_clock_refresh_freq * 1000 ]
         after $run_after Update_Clock
      } else {
         set run_after 10000
         after $run_after Update_Clock
      }

      if {$gb_ping_connection == 1} {
         # Keep the connection alive
         eval exec $g_ping_command >>& $g_log_file &
      }
   } else {

      # Connection has gone down (use pixmap if specified)
      if {$g_disconnected_pixmap != ""} {
         set Image [Create_Image $g_disconnected_pixmap]
         if {$Image == -1} {
            $g_connection_status configure -text  "Disconnected" \
               -background $GREEN -highlightbackground $GREEN
         } else {
            $g_connection_status configure -image $Image \
               -highlightbackground $GREEN
         }
      } else {
            $g_connection_status configure -text  "Disconnected" \
               -background $GREEN -highlightbackground $GREEN
      }
         
      Set_Message "Connection lost!" $RED
      wm iconname . "Net Down"

      # Beep if option set
      if {$gb_beep_on_connect == 1} {
         bell
         bell
      }

      # If the option is set, attempt to reconnect 
      # immediately
      if {$gb_auto_reconnect == 1} {
         Net_Connect
      }

      # Set the icon
      # wm iconbitmap . @/usr/include/X11/pixmaps/netdn.xpm
   }
}

###############################################################
# The procedure for updating the throughput statistics
proc Update_Statistics {} {
   Debug "Update_Statistics"

   # Globals
   global gb_connection_down g_link_stats \
      gt_last_stat_time g_previous_stat \
      g_throughput g_idle_disconnect_time \
      RED g_network_interface

   # Only update statistics if connected
   Set_Connection_Flag

   if {$gb_connection_down == 0} {

      # Two times are used.  A previous time indication
      # stored in gt_last_stat_time along with the 
      # result of the ifconfig command, g_previous_stat.
      # These are compared against the new values which 
      # are stored in t_stat_time and stat_value

      # Retrieve current throughput data
      set xmit [exec cat /proc/net/dev | \
         grep $g_network_interface | awk {{print $7}}]
      set rec [exec cat /proc/net/dev | \
         grep $g_network_interface | awk {{print $2}}]
      set stat_value [expr $xmit + $rec]
      
      # Get current time stamp
      set t_stat_time [Get_Current_Time]

      # If this is the first time round, store
      # the time and value and return without 
      # calculating.
      if {$gt_last_stat_time == 0} {
         set g_previous_stat $stat_value
         set gt_last_stat_time $t_stat_time
         after 5000 Update_Statistics
         return
      }

      # Perform the throughput calculation - note the
      # introduction of a floating point numbe to 
      # improve the accuracy of the result
      set difftime [AddSub_Time $gt_last_stat_time \
         $t_stat_time -1 1]
      # Check for divide by zero
      if {$difftime == 0} {
         set g_previous_stat $stat_value
         set gt_last_stat_time $t_stat_time
         after 5000 Update_Statistics
         return
      }
      set throughput [expr ( $stat_value - \
         $g_previous_stat ) / ( $difftime + 0.0 )]
      $g_throughput configure -text "$throughput Pkts/s"

      # If idle checking is enabled, perform that
      # here (as we've already done the work)
      if {$g_idle_disconnect_time > 0} {
         if {$throughput == 0} {
            incr gt_idle_time $difftime
            if {[expr $g_idle_time / 60.0] > gt_idle_disconnect_time} {
               Net_Disconnect
               after 5000 Set_Message "Link idle - diconnected" $RED
            }
         } else {
            set gt_idle_time 0
         }
      }

      # Copy the new stat time and value to the
      # old variables
      set gt_last_stat_time $t_stat_time
      set g_previous_stat $stat_value

   } else {
      # Reset count for next connection
      set gt_last_stat_time 0
      # Update string
      $g_throughput configure -text "0.00 Pkts/s"
   }

   # Rerun after N secs if time is > 0
   if {$g_link_stats > 0} {
      set run_after [ expr $g_link_stats * 1000 ]
      after $run_after Update_Statistics
   } else {
      # Run after 1 minute for the idle check
      after 60000 Update_Statistics
   }
}

###############################################################
# The procedure for quitting
proc Quit { } {
   Debug "Quit"

   # Globals
   global gb_connection_down RED gb_confirm_quit

   # Set_Message "Quitting ..." $RED
   if {$gb_connection_down == 0} {

      # Ask the user whether to disconnect and
      # quit
      if ![Question_Dialog . \
         "You are still connected!  Do you want to \
disconnect and quit?" "Yes" "No"] {

         # Disconnect from network
         Net_Disconnect

         # Exit program
         exit
      }
   } else {

      if {$gb_confirm_quit && \
         ![Question_Dialog . "Do you really want to quit?" \
         "Yes" "No"]} {
         
         # Exit program
         exit
      }
      
      if {! $gb_confirm_quit} {
         # Exit program
         exit
      }
   }
}

###############################################################
# The procedure for creating the main popup menu
proc Create_Popup_Menu {} {
   Debug "Create_Popup_Menu"

   global FONT_NORMAL g_spool_file_size

   # Check for existence
   if [winfo exists .popup_menu] {destroy .popup_menu}

   # Create the popup menu
   menu .popup_menu
      Create_Connect_Menu_Items .popup_menu
      Create_Disconnect_Menu_Items .popup_menu
      .popup_menu add command -label "Deliver Mail" -command { \
         Deliver_Mail } -accelerator "Ctrl-M" -font \
         $FONT_NORMAL -underline 8
      .popup_menu add command -label "Biff (check mail)" \
         -command { set g_spool_file_size 0 ; Biff 1} \
         -accelerator "Ctrl-B" -font $FONT_NORMAL -underline 0
      .popup_menu add separator
      .popup_menu add command -label "View Log ..." -command \
         { Show_Log } -font $FONT_NORMAL -underline 0
      .popup_menu add command -label "Mail ..." -command \
         { Show_Mail } -font $FONT_NORMAL -underline 0
      .popup_menu add separator
      .popup_menu add command -label "Quit" \
         -accelerator "Ctrl-Q" -command { Quit } -font \
         $FONT_NORMAL -underline 0

   # Add a title for the tearoff
   wm title .popup_menu TkNet
}

###############################################################
# The procedure for creating the main menu
proc Create_File_Menu {} {
   Debug "Create_File_Menu"

   global FONT_NORMAL

   # Check for existence
   if [winfo exists .mbar.file.menu] {destroy .mbar.file.menu}

   menu .mbar.file.menu -tearoff 0
      Create_Connect_Menu_Items .mbar.file.menu
      Create_Disconnect_Menu_Items .mbar.file.menu
      .mbar.file.menu add command -label "Deliver Mail" -command { \
         Deliver_Mail } -font $FONT_NORMAL -underline 8
      .mbar.file.menu add command -label "Biff (check mail)" \
         -command { set g_spool_file_size 0 ; Biff 1} \
         -font $FONT_NORMAL -underline 0
      .mbar.file.menu add separator
      .mbar.file.menu add command -label "Quit" \
         -command { Quit } -font $FONT_NORMAL -underline 0
}
   
###############################################################
# The procedure for creating the connect items on both the
# fie menu and the popup menu
proc Create_Connect_Menu_Items { base } {
   Debug "Create_Connect_Manu_Items"

   global FONT_NORMAL g_script_number g_current_script

   # Create the popup menu
   for {set count 0} {$count < $g_script_number} {incr count} \
   {
      # Define globals
      global "g_script_label_$count" \
         "g_script_script_$count"

      # Create the label and the accelerator
      set label [eval subst {\$g_script_label_$count}]
      set accel [join [list "Ctrl-" [expr $count + 1]] ""]
      set script [eval subst {\$g_script_script_$count}]

      # Create a row for the item
      $base add radiobutton -label $label -command " \
         set g_current_script $count; Net_Connect" \
         -accelerator $accel -font $FONT_NORMAL -variable \
         g_current_script -value $count
   }
   $base add separator
}

###############################################################
# The procedure for creating the disconnect items on both the
# file menu and the popup menu
proc Create_Disconnect_Menu_Items { base } {
   Debug "Create_Disconnect_Manu_Items"

   global FONT_NORMAL g_script_number g_current_script

   # Create the submenu
   $base add cascade -label Disconnect -font $FONT_NORMAL \
      -underline 0 -menu $base.disconnect
   menu $base.disconnect -tearoff 0

   # Now for the items
   for {set count 0} {$count < $g_script_number} {incr count} \
   {
      # Define globals
      global "g_script_label_$count" \
         "g_script_disconnect_$count"

      # Create the label and the accelerator
      set label [eval subst {\$g_script_label_$count}]
      set script [eval subst {\$g_script_disconnect_$count}]

      # Create a row for the item
      $base.disconnect add radiobutton -label $label \
         -command "set g_current_script $count; Net_Disconnect" \
         -font $FONT_NORMAL -variable \
         g_current_script -value $count
   }
   $base add separator
}

###############################################################
# The procedure for popping up a popup menu
proc Popup_Menu { parent window } {
   Debug "Popup_Menu"

   # Get current mouse position
   set x [ winfo pointerx $parent ]
   set y [ winfo pointery $parent ]

   # Popup the menu
   tk_popup $window $x $y
}

###############################################################
# Show the log panel
proc Show_Log { } {
   Debug "Show_Log"

   # Globals
   global FONT_NORMAL FIXED_FONT_SMALL RIDGE_BORDER \
      DEFAULT_PADDING g_log_messages TKNET_LOG_GEOMETRY \
      g_log_file g_log_file_id

   # Create the log toplevel as a transient from the main window
   if [winfo exists .log_window] return
   toplevel .log_window
   wm title .log_window "View Log"
   wm transient .log_window .
   wm geometry .log_window $TKNET_LOG_GEOMETRY
   wm protocol .log_window WM_DELETE_WINDOW Close_Log_Window

   ###############################################################
   # Create the log panel
   frame .log_window.log_fr -borderwidth $RIDGE_BORDER -relief groove
   pack .log_window.log_fr -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING -side top -expand true -fill both
   message .log_window.log_fr.log_label -text "Messages" \
      -font $FONT_NORMAL -aspect 400
   pack .log_window.log_fr.log_label -side top -anchor w
   set g_log_messages [ScrolledText .log_window.log_fr.log_st \
      60 20 1]
   $g_log_messages configure -takefocus 0 -font $FIXED_FONT_SMALL

   ###############################################################
   # Create the buttons below the frame
   frame .log_window.button_frame -borderwidth $DEFAULT_PADDING
   pack .log_window.button_frame -side bottom -fill x
   button .log_window.button_frame.close -font $FONT_NORMAL \
       -text Close -command { destroy .log_window }
   pack .log_window.button_frame.close -side right

   ###############################################################
   # Read the log file
   Read_Log_File
}

###############################################################
# The procedure for reading from the log file
proc Read_Log_File {} {
   # Debug "Read_Log_File"

   # Globals
   global g_log_file g_log_file_size g_log_messages

   #Info_Dialog . "Reading data"
   if {[winfo exists $g_log_messages]} {

      # Read the file data if more available
      set log_file_size [file size $g_log_file]
      if {$log_file_size > $g_log_file_size} {
         $g_log_messages configure -state normal
         $g_log_messages delete 1.0 end
         $g_log_messages insert end [eval exec \
            tail -30 $g_log_file]
         $g_log_messages insert end \n
         $g_log_messages see end
         $g_log_messages configure -state disabled
      }
      set g_log_file_size $log_file_size
      after 5000 Read_Log_File
   }
}

###############################################################
# The procedure for quitting the log window
proc Close_Log_Window {} {
   Debug "Close_Log_Window"

   # Globals
   global g_read_log_file_id g_log_file_size

   # Ensure that when the window is reopened the file is read
   set g_log_file_size 0

   # Destroy the window
   destroy .log_window 
}

###############################################################
# Show the mail panel
proc Show_Mail { } {
   Debug "Show_Mail"

   # Globals
   global gt_incoming_mail gt_outgoing_mail FONT_NORMAL \
      FIXED_FONT_SMALL RIDGE_BORDER DEFAULT_PADDING GREEN \
      gb_mail_window_up gs_incoming_mail gs_outgoing_mail \
      TKNET_MAIL_GEOMETRY

   # Create the mail toplevel as a transient from the main window
   if [winfo exists .mail_window] return
   toplevel .mail_window
   wm title .mail_window "Manage Mail"
   wm transient .mail_window .
   wm geometry .mail_window $TKNET_MAIL_GEOMETRY
   wm protocol .mail_window WM_DELETE_WINDOW Close_Mail_Window

   # The mail window comprises three areas, plus menus + buttons
   # A menu bar duplicates the buttons and provides close
   # functions.  The next area is a list of outgoing mail -
   # use sendmail -bq?   The third area is a list of mail
   # waiting on the server - ping siren@post.demon.co.uk.
   # The fourth area is a message line which indicates the
   # current state of the connection, i.e. network down,
   # SMTP connections made, no mail left on demon, no
   # mail left to deliver etc.  At the bottom of the
   # screen is a button bar which allows exmh to be started
   # and the window to be dismissed.

   ###########################################################################
   # Create Menu Bar
   frame .mail_window.mbar -relief raised -bd 2
   frame .mail_window.dummy -width 10c -height 5m
   pack .mail_window.mbar .mail_window.dummy -side top -fill x

   menubutton .mail_window.mbar.file -text File -font $FONT_NORMAL \
      -underline 0 \
      -menu .mail_window.mbar.file.menu
   menubutton .mail_window.mbar.mail -text Mail -font $FONT_NORMAL \
      -underline 0 \
      -menu .mail_window.mbar.mail.menu
   pack .mail_window.mbar.file .mail_window.mbar.mail -side left

   menu .mail_window.mbar.file.menu -tearoff 0
      .mail_window.mbar.file.menu add command -label "Close" \
         -command { Close_Mail_Window } -font $FONT_NORMAL \
         -underline 0

   menu .mail_window.mbar.mail.menu -tearoff 0
      .mail_window.mbar.mail.menu add command -label \
         "Deliver Queued Mail" -command { Run_Mail_Queue } \
         -font $FONT_NORMAL -underline 0
      .mail_window.mbar.mail.menu add command -label \
         "Start Mailtool" -command { Run_Mail_Tool } \
         -font $FONT_NORMAL -underline 0

   tk_menuBar .mail_window.mbar .mail_window.mbar.file \
      .mail_window.mbar.mail

   ###############################################################
   # Create the incoming mail panel
   frame .mail_window.incoming_fr -borderwidth $RIDGE_BORDER \
      -relief groove
   pack .mail_window.incoming_fr -padx $DEFAULT_PADDING \
      -pady $DEFAULT_PADDING -side top -expand true \
      -fill both
   message .mail_window.incoming_fr.label -text "Incoming Mail" \
      -font $FONT_NORMAL -aspect 400
   pack .mail_window.incoming_fr.label -side top -anchor w
   set gt_incoming_mail [ScrolledText \
      .mail_window.incoming_fr.log_st 80 10 1]
   $gt_incoming_mail configure -takefocus 0 -font \
      $FIXED_FONT_SMALL

   ###############################################################
   # Create the outgoing mail panel
   frame .mail_window.outgoing_fr -borderwidth $RIDGE_BORDER \
      -relief groove
   pack .mail_window.outgoing_fr -padx $DEFAULT_PADDING \
      -pady $DEFAULT_PADDING -side top -expand true \
      -fill both
   message .mail_window.outgoing_fr.label -text "Outgoing Mail" \
      -font $FONT_NORMAL -aspect 400
   pack .mail_window.outgoing_fr.label -side top -anchor w
   set gt_outgoing_mail [ScrolledText \
      .mail_window.outgoing_fr.log_st 80 5 1]
   $gt_outgoing_mail configure -takefocus 0 \
      -font $FIXED_FONT_SMALL

   ###############################################################
   # Create the buttons below the frame
   frame .mail_window.button_frame -borderwidth $DEFAULT_PADDING
   pack .mail_window.button_frame -side bottom -fill x
   button .mail_window.button_frame.close -text Close \
      -font $FONT_NORMAL \
      -command { Close_Mail_Window }
   button .mail_window.button_frame.mailtool -font $FONT_NORMAL \
       -text "Mailtool ..." -command { Run_Mail_Tool }
   pack .mail_window.button_frame.close \
      .mail_window.button_frame.mailtool -side right

   ###############################################################
   # Startup the background processing
   set gb_mail_window_up 1
   Background_Mail_Check
}

###############################################################
# Run the mail queue
proc Run_Mail_Queue { } {
   Debug "Run_Mail_Queue"

   # Globals
   global g_runq_command GREEN RED g_log_file

   # Set global hourglassing
   WatchCursor

   # Run the sendmail queue
   if [catch { eval exec $g_runq_command >>& $g_log_file } \
      error_text] {
      Set_Message "Unable to run mail queue" $RED
   } else {
      Set_Message "Mail queue run successfully" $GREEN
   }

   # Undo hourglassing
   NormalCursor
}

###############################################################
# Run mailtool (exmh)
proc Run_Mail_Tool { } {
   Debug "Run_Mail_Tool"

   # Globals
   global g_mailtool GREEN RED g_log_file

   # Set global hourglassing
   WatchCursor

   # Run the mailtool
   if [catch { eval exec $g_mailtool >>& $g_log_file & } \
      error_text] {
      Set_Message "Unable to start $g_mailtool" $RED
   } else {
      Set_Message "Mailtool starting ..." $GREEN
   }

   # Undo hourglassing
   NormalCursor
}

###############################################################
# The procedure for checking mail
# If manual_flag is 1, message is written even when no new mail
proc Biff { manual_flag } {
   Debug "Biff"

   # Globals
   global gs_spool_dir g_spool_file_size gb_beep_on_new_mail \
      TKNET_USER g_biff_check RED gb_check_for_mail

   # Now check the TKNET_USER's spool file for new mail
   if {$gb_check_for_mail == 1 || $manual_flag == 1} {
      set spool_size [ file size [ join [ list $gs_spool_dir \
         "/" $TKNET_USER ] "" ] ]
      if {$manual_flag == 1} {set g_spool_file_size 0}
      if { $spool_size > $g_spool_file_size } {
   
         # Spool file has new data in it
         Set_Message "YOU HAVE NEW MAIL!" $RED
   
         # Beep if option set
         if {$gb_beep_on_new_mail == 1} {
            bell
         }
      } elseif {$manual_flag == 1} {
         # Spool file has no new data in it
         Set_Message "NO NEW MAIL!" $RED
      }

      # Remember the new size
      set g_spool_file_size $spool_size
   }

   # Recall after N secs
   if {$g_biff_check > 0} {
      set run_after [ expr $g_biff_check * 1000 ]
      after $run_after Biff 0
   }
}

###############################################################
# The procedure for checking mail
proc Background_Mail_Check {} {
   Debug "Background_Mail_Check"

   # Globals
   global gb_connection_down g_mail_refresh_freq gt_incoming_mail \
      gt_outgoing_mail g_check_remote_mail g_check_local_mail \
      GREEN RED gb_mail_window_up gb_delivering_mail \
      gs_incoming_mail gs_outgoing_mail gs_remote_mail_indicator \
      gs_local_mail_indicator gt_incoming_mail gt_outgoing_mail \
      gs_spool_dir g_spool_file_size gb_beep_on_new_mail \
      TKNET_USER g_log_file

   # Only perform this processing if the mail window
   # is still active
   if {$gb_mail_window_up == 1} {

      # Set global hourglassing
      WatchCursor
   
      # Only get remote info if connected
      Set_Connection_Flag
   
      if {$gb_connection_down == 0} {
   
         # Remote mail i.e. incoming
         catch { eval exec $g_check_remote_mail} \
            gs_incoming_mail

         # Display in textbox
         $gt_incoming_mail delete 1.0 end
         $gt_incoming_mail insert end $gs_incoming_mail
         $gt_incoming_mail insert end "\n"

      } else {
   
         # Log the fact that remote mail info is unavailable
         $gt_incoming_mail delete 1.0 end
         $gt_incoming_mail insert end "Incoming mail information is unavailable"
         $gt_incoming_mail insert end "\n"
      }
   
      # Check for local mail
      catch { eval exec $g_check_local_mail} \
         gs_outgoing_mail
   
      # Display in textbox
      $gt_outgoing_mail delete 1.0 end
      $gt_outgoing_mail insert end $gs_outgoing_mail
      $gt_outgoing_mail insert end "\n"

      # Rerun after N secs
      if {$g_mail_refresh_freq > 0} {
         set run_after [ expr $g_mail_refresh_freq * 1000 ]
         after $run_after Background_Mail_Check
      } else {
         after 30000 Background_Mail_Check
      }

      # This routine not only fetches and displays information
      # about mail to be delivered locally and mail being held
      # on the remote server, but also determines whether there
      # is any mail left to deliver.  When the gb_delivering_mail
      # flag is set, after the routine has determined that there
      # is no more mail to deliver, it closes the window and
      # the netowrk connection.
      if {$gb_connection_down == 0 && \
         $gb_delivering_mail == 1} {

         # Note : both checks must FAIL, i.e. the strings
         # given in the configuration section are strings
         # that are known to appear when mail is present 
         # on the servers.
         if { [ string first $gs_local_mail_indicator \
            $gs_outgoing_mail ] == -1 && \
            [ string first $gs_remote_mail_indicator \
            $gs_incoming_mail ] == -1} {

            # No more messages to deliver
            Set_Message \
               "No more messages to deliver - disconnecting" \
               $GREEN
 
            # Disconnect from network
            Net_Disconnect

            # Close the mail window
            Close_Mail_Window
         }
      }

      # Undo hourglassing
      NormalCursor
   }
}

###############################################################
# The procedure for quitting the mail window
proc Close_Mail_Window {} {
   Debug "Close_Mail_Window"

   # Globals
   global gb_mail_window_up

   # Reset the flag
   set gb_mail_window_up 0

   # Destroy the window
   destroy .mail_window 
}

###############################################################
# The procedure for setting the connection flag
proc Set_Connection_Flag {} {
   # Debug "Set_Connection_Flag"

   # Globals
   global gb_connection_down g_network_interface

   # Check the connection, and set the result into the
   # global variable for use by other scripts
   set gb_connection_down [ catch {eval exec /sbin/ifconfig \
| grep $g_network_interface} error_text ]
}

###############################################################
# The procedure for checking whether the network is connected
proc Check_For_Connection {} {
   Debug "Check_For_Connection"

   # Globals
   global gb_connection_down gb_connection_down_prev \
      gb_waiting_for_connection g_network_check_freq \
      g_connection_status g_wait_time g_max_wait_time \
      g_connect_process GREEN RED gb_beep_on_connect \
      gb_reset_clock_on_connect g_connection_time \
      HELP_COLOUR g_connected_pixmap gt_time_connected \
      gt_saved_time

   # If the connected flag is set, start the clock update,
   # otherwise call yourself again in a few seconds.   If
   # a cetain amount of time has elapsed since the request
   # to connect to the network, disconnect, display an
   # error and retry.
   Set_Connection_Flag

   if {$gb_connection_down == 0 && \
      $gb_connection_down_prev == 1} {

      # Set the initial connect time (if not specified)
      set gt_time_connected [Get_Current_Time]

      # Set the big green label
      if {$g_connected_pixmap != ""} {
         set Image [Create_Image $g_connected_pixmap]
         if {$Image == -1} {
            $g_connection_status configure -text "Connected" \
               -background $RED -highlightbackground $RED
         } else {
            $g_connection_status configure -image $Image \
               -highlightbackground $RED
         }
      } else {
         $g_connection_status configure -text "Connected" \
            -background $RED -highlightbackground $RED
      }

      # Beep if option set
      if {$gb_beep_on_connect == 1} {
         bell
         bell
      }

      # Save the time if option set
      if {$gb_reset_clock_on_connect == 0} {
         set $gt_time_connected [Get_Current_Time]
         set gt_saved_time [$g_connection_time cget -text]
      } else {
         set gt_saved_time "000/00:00:00"
      }

      # Set the icon
      # wm iconbitmap . @/usr/include/X11/pixmaps/netup.xpm

      # Reset the waiting for connection flag
      set gb_waiting_for_connection 0
      set g_wait_time 0

      # Start the clock update
      Update_Clock

      # Set a connected message
      Set_Message "New connection detected" $GREEN
      wm iconname . "Net Up"
   } 

   if {$gb_connection_down == 1 && \
      $gb_waiting_for_connection == 1} {

      # Set a message
      # Debug "Waiting for connection..."
      set message [join [list "Waiting for connection (" \
         $g_wait_time " secs)"] ""]
      Set_Message $message $HELP_COLOUR

      # Check against max allowable wait time
      set g_wait_time [ expr $g_wait_time + $g_network_check_freq ]
      if {$g_wait_time > $g_max_wait_time} {

         # Display a message
         Set_Message "Given up waiting for connection!" \
            $RED

         # Max wait time exceeded
         set process_running [ Is_Process_Running \
            $g_connect_process ]

         # Try to kill it or otherwise stop it
         if {$process_running == 1} {
            # Kill_Process $g_connect_process
            Net_Disconnect
         }

         # Retry connecting
         Net_Connect
      } 
   }

   # Call routine again after N secs
   if {$g_network_check_freq > 0} {
      set run_after [ expr $g_network_check_freq * 1000 ]
   } else {
      set run_after 10000
   }
   after $run_after Check_For_Connection

   # Set the flag which remembers the previous state
   set gb_connection_down_prev $gb_connection_down
}

###############################################################
# The procedure for checking whether a process is running
proc Is_Process_Running { process } {
   Debug "Is_Process_Running"

   # The procedure returns 1 if the process is still
   # running and 0 otherwise.
   global g_status

   # puts "Checking for process ..."
   set g_status -1
   catch {eval exec sh -c "kill -0 $process >& /dev/null; \
      echo $?"} result
   if { $result == 0 } {
      set g_status 1
      return $g_status
   } else {
      set g_status 0
      return $g_status
   }
}

###############################################################
# The procedure for killing a process
proc Kill_Process { process } {
   Debug "Kill_Process"

   # Globals
   global g_log_file

   # Attempt to kill the process.  Note that this won't
   # work if DIP is running setuid.  Don't bother
   # displaying the error info though...
   catch {eval exec kill -TERM $process >>& $g_log_file} \
      error_text
}

###############################################################
# Change cursor to an hourglass and back again
proc WatchCursor {} {
   # Debug "WatchCursor"

   # Loop through children setting the cursor
        foreach w [winfo children .] {
                lappend busy [list $w [lindex [$w config -cursor] 4]]
        }
        foreach w $busy {catch {[lindex $w 0] config -cursor watch}}
        update idletasks
}
proc NormalCursor {} {
   # Debug "NormalCursor"

   # Loop through children setting the cursor
        foreach w [winfo children .] {
                lappend notbusy [list $w [lindex [$w config -cursor] 4]]
        }
        foreach w $notbusy {catch {[lindex $w 0] config -cursor hand2}}
        update idletasks
}

###############################################################
# Set a message into the main window message line
proc Set_Message { text colour } {
   # Debug "Set_Message"

   # Globals
   global HELP_COLOUR g_log_file gi_clear_message \
      g_message_display_time

   # Cancel current clear command
   after cancel $gi_clear_message

   # Set the text input into the message line
   .message_text configure -text $text -foreground \
      $colour

   if [string compare $colour $HELP_COLOUR] {

      # Log the message to file (if not help)
      set log_file_id [ open $g_log_file a+ ]

      # Write the message to file
      puts $log_file_id $text
      flush $log_file_id

      # Close log file
      close $log_file_id
   }

   # Set a timer to clear the message after 10 secs
   if {$text != "" && $g_message_display_time > 0} {
      set run_after [expr $g_message_display_time * 1000]
      set gi_clear_message [after $run_after \
          {Set_Message "" $HELP_COLOUR}]
   }
}

###############################################################
# Set a microhelp message into the main window message line
proc Set_Microhelp { text } {
   # Debug "Set_Microhelp"

   # Globals
   global gb_show_microhelp HELP_COLOUR
   
   # Test the global flag
   if {$gb_show_microhelp == 1} {
      Set_Message $text $HELP_COLOUR
   }
}

###############################################################
# Routine to show (or hide) the time display
proc Show_Time { show } {
   Debug "Show_Time"

   if {$show == 1} {
      # Already created, just pack it
      pack .status_fr.time_fr -after .status_fr.connect_fr \
         -side top -anchor w
      wm geometry . ""
   } else {
      # Already managed, hide it!
      pack forget .status_fr.time_fr
      wm geometry . ""
   }
}

###############################################################
# Routine to show (or hide) the messages display
proc Show_Messages { show } {
   Debug "Show_Messages"

   global DEFAULT_PADDING

   if {$show == 1} {
      # Already created, just pack it
      pack .message_text -side top -padx $DEFAULT_PADDING \
         -pady $DEFAULT_PADDING -fill x
      wm geometry . ""
   } else {
      # Already managed, hide it!
      pack forget .message_text
      wm geometry . ""
   }
}

###############################################################
# Routine to show (or hide) the buttons
proc Show_Buttons { show } {
   Debug "Show_Buttons"

   if {$show == 1} {
      # Already created, just pack it
      pack .button_frame -side bottom -fill x
      wm geometry . ""
   } else {
      # Already managed, hide it!
      pack forget .button_frame
      wm geometry . ""
   }
}

###############################################################
# Routine to show (or hide) the buttons
proc Show_Stats { show } {
   Debug "Show_Stats"

   if {$show == 1} {
      # Already created, just pack it
      pack .status_fr.stats_frame -side bottom -fill x \
         -after .status_fr.connect_fr
      wm geometry . ""
   } else {
      # Already managed, hide it!
      pack forget .status_fr.stats_frame
      wm geometry . ""
   }
}

###############################################################
# Show the setup panel
proc Show_Setup { } {
   Debug "Show_Setup"

   # Globals
   global FONT_NORMAL FONT_BOLD DEFAULT_PADDING RIDGE_BORDER \
      gs_ping_command_text g_ping_command \
      gs_ping_command_text_help \
      gs_runq_command_text g_runq_command \
      gs_runq_command_text_help \
      gs_mailtool_text g_mailtool \
      gs_mailtool_text_help \
      gs_network_interface_text g_network_interface \
      gs_network_interface_text_help \
      gs_mail_refresh_freq_text g_mail_refresh_freq \
      gs_mail_refresh_freq_text_help \
      gs_check_remote_mail_text g_check_remote_mail \
      gs_check_remote_mail_text_help \
      gs_check_local_mail_text g_check_local_mail \
      gs_check_local_mail_text_help \
      gs_clock_refresh_freq_text g_clock_refresh_freq \
      gs_clock_refresh_freq_text_help \
      gs_log_file_text g_log_file \
      gs_log_file_text_help \
      gs_network_check_freq_text g_network_check_freq \
      gs_network_check_freq_text_help \
      gs_max_wait_time_text g_max_wait_time \
      gs_max_wait_time_text_help \
      gs_remote_mail_indicator_text gs_remote_mail_indicator \
      gs_remote_mail_indicator_text_help \
      gs_local_mail_indicator_text gs_local_mail_indicator \
      gs_local_mail_indicator_text_help \
      gs_spool_dir_text gs_spool_dir \
      gs_spool_dir_text_help g_option_number \
      g_setup_changed TKNET_SETUP_GEOMETRY \
      g_message_display_time_text g_message_display_time \
      g_message_display_time_text_help \
      g_connected_pixmap_text g_connected_pixmap \
      g_connected_pixmap_text_help \
      g_disconnected_pixmap_text g_disconnected_pixmap \
      g_disconnected_pixmap_text_help \
      g_link_stats_text g_link_stats g_link_stats_text_help \
      g_idle_disconnect_time_text g_idle_disconnect_time \
      g_idle_disconnect_time_text_help \
      g_separator_number \
      g_biff_check_text g_biff_check gs_biff_check_text_help

   # Create the setup toplevel as a transient from the main window
   if [winfo exists .setup_window] return
   toplevel .setup_window
   wm title .setup_window "TkNet Setup"
   wm transient .setup_window .
   wm geometry .setup_window $TKNET_SETUP_GEOMETRY
   wm protocol .setup_window WM_DELETE_WINDOW Close_Setup_Window

   ###########################################################################
   # Create Menu Bar
   frame .setup_window.mbar -relief raised -bd 2
   pack .setup_window.mbar -side top -fill x

   menubutton .setup_window.mbar.file -text File -font $FONT_NORMAL \
      -underline 0 \
      -menu .setup_window.mbar.file.menu
   pack .setup_window.mbar.file -side left

   menu .setup_window.mbar.file.menu -tearoff 0
      .setup_window.mbar.file.menu add command -label "Save" \
         -command { Save_Setup } -font $FONT_NORMAL \
         -underline 0
      .setup_window.mbar.file.menu add command -label \
         "Set Defaults from Global" -command \
         { set g_setup_changed 1 ; Source_Setup 1 } \
         -font $FONT_NORMAL -underline 4
      .setup_window.mbar.file.menu add separator
      .setup_window.mbar.file.menu add command -label "Close" \
         -command { Close_Setup_Window } -font $FONT_NORMAL \
         -underline 0

   tk_menuBar .setup_window.mbar .setup_window.mbar.file

   ###############################################################
   # Create the main panel
   label .setup_window.label -text "Enter the configuration \
items necessary for your setup in the fields below.  To obtain \
help on any item, click on the corresponding label." -font \
      $FONT_NORMAL -wraplength 500 -justify center
   pack .setup_window.label -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING -side top -fill x
   frame .setup_window.setup_fr -borderwidth $RIDGE_BORDER \
      -relief groove 
   pack .setup_window.setup_fr -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING -side top -expand true -fill both
   canvas .setup_window.setup_fr.c -yscrollcom \
      ".setup_window.setup_fr.sy set" -scrollregion \
      "0 0 600 0" -width 600 -highlightthickness 0
   pack [scrollbar .setup_window.setup_fr.sy -orient v \
      -com ".setup_window.setup_fr.c yview"] \
      -side right -fill y
   pack .setup_window.setup_fr.c -padx $DEFAULT_PADDING \
      -pady $DEFAULT_PADDING -side top -fill both \
      -expand true -exp 1

   ###############################################################
   # Create the individual settings
   set g_option_number 0
   set g_separator_number 0
   Create_Option_Separator "Commands"
   Create_Option_Entry $gs_ping_command_text \
      g_ping_command gs_ping_command_text_help
   Create_Option_Entry $gs_runq_command_text \
      g_runq_command gs_runq_command_text_help
   Create_Option_Entry $gs_mailtool_text \
      g_mailtool gs_mailtool_text_help
   Create_Option_Separator "Network Settings"
   Create_Option_Entry $gs_network_interface_text \
      g_network_interface gs_network_interface_text_help
   Create_Option_Entry $gs_network_check_freq_text \
      g_network_check_freq gs_network_check_freq_text_help
   Create_Option_Entry $gs_max_wait_time_text \
      g_max_wait_time gs_max_wait_time_text_help
   Create_Option_Entry $g_connected_pixmap_text \
      g_connected_pixmap g_connected_pixmap_text_help
   Create_Option_Entry $g_disconnected_pixmap_text \
      g_disconnected_pixmap g_disconnected_pixmap_text_help
   Create_Option_Entry $g_link_stats_text \
      g_link_stats g_link_stats_text_help
   Create_Option_Entry $g_idle_disconnect_time_text \
      g_idle_disconnect_time g_idle_disconnect_time_text_help
   Create_Option_Separator "Mail Settings"
   Create_Option_Entry $gs_mail_refresh_freq_text \
      g_mail_refresh_freq gs_mail_refresh_freq_text_help
   Create_Option_Entry $g_biff_check_text \
      g_biff_check gs_biff_check_text_help
   Create_Option_Entry $gs_check_remote_mail_text \
      g_check_remote_mail gs_check_remote_mail_text_help
   Create_Option_Entry $gs_check_local_mail_text \
      g_check_local_mail gs_check_local_mail_text_help
   Create_Option_Entry $gs_remote_mail_indicator_text \
      gs_remote_mail_indicator gs_remote_mail_indicator_text_help
   Create_Option_Entry $gs_local_mail_indicator_text \
      gs_local_mail_indicator gs_local_mail_indicator_text_help
   Create_Option_Entry $gs_spool_dir_text \
      gs_spool_dir gs_spool_dir_text_help
   Create_Option_Separator "Other Settings"
   Create_Option_Entry $gs_clock_refresh_freq_text \
      g_clock_refresh_freq gs_clock_refresh_freq_text_help
   Create_Option_Entry $gs_log_file_text \
      g_log_file gs_log_file_text_help
   Create_Option_Entry $g_message_display_time_text \
      g_message_display_time g_message_display_time_text_help

   frame .setup_window.button_frame -borderwidth $DEFAULT_PADDING
   pack .setup_window.button_frame -side bottom -fill x
   button .setup_window.button_frame.close -font $FONT_NORMAL \
       -text Close -command { Close_Setup_Window }
   pack .setup_window.button_frame.close
}

###############################################################
# Create a button, entry pair
proc Create_Option_Entry {text variable help} {
   Debug "Create_Option_Entry"

   # Globals
   global FONT_NORMAL DEFAULT_PADDING g_option_number \
      OPTION_TEXT_LENGTH OPTION_LABEL_LENGTH \
      g_setup_changed $help

   # Work out the canvas height
   set canvas_height [.setup_window.setup_fr.c cget \
      -scrollregion]
   scan $canvas_height "%*d %*d %*d %d" canvas_height
   set canvas_height [expr $canvas_height + 40]
   .setup_window.setup_fr.c configure -scrollregion \
      "0 0 600 $canvas_height"

   # Create a label/textentry pair
   frame .setup_window.setup_fr.c.$g_option_number
   button .setup_window.setup_fr.c.$g_option_number.b -text \
      $text -font $FONT_NORMAL -width $OPTION_LABEL_LENGTH \
       -relief flat -command "Mini_Help $help"
   entry .setup_window.setup_fr.c.$g_option_number.e \
      -font $FONT_NORMAL -textvariable $variable \
      -width $OPTION_TEXT_LENGTH
   bind .setup_window.setup_fr.c.$g_option_number.e \
      <KeyPress> { set g_setup_changed 1 }
   pack .setup_window.setup_fr.c.$g_option_number.b \
      .setup_window.setup_fr.c.$g_option_number.e -side left \
      -padx $DEFAULT_PADDING -pady $DEFAULT_PADDING

   set offset [expr $canvas_height -40]
   .setup_window.setup_fr.c create window 15 $offset \
      -window .setup_window.setup_fr.c.$g_option_number \
      -anchor nw

   # Increment option number
   incr g_option_number
}

###############################################################
# Create a button, entry pair
proc Create_Option_Separator {text} {
   Debug "Create_Option_Separator"

   # Globals
   global FONT_BOLD DEFAULT_PADDING g_separator_number

   # Work out the canvas height
   set canvas_height [.setup_window.setup_fr.c cget \
      -scrollregion]
   scan $canvas_height "%*d %*d %*d %d" canvas_height
   set canvas_height [expr $canvas_height + 40]
   .setup_window.setup_fr.c configure -scrollregion \
      "0 0 600 $canvas_height"

   # Create a label/textentry pair
   frame .setup_window.setup_fr.c.s$g_separator_number
   label .setup_window.setup_fr.c.s$g_separator_number.l \
      -text "_______________________ $text \
_______________________" -font $FONT_BOLD
   pack .setup_window.setup_fr.c.s$g_separator_number.l \
      -fill x -side top -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING
   set offset [expr $canvas_height -40]
   .setup_window.setup_fr.c create window 15 $offset \
      -window .setup_window.setup_fr.c.s$g_separator_number \
      -anchor nw -width 600

   # Increment option number
   incr g_separator_number
}

###############################################################
# Show the setup panel
proc Show_Launcher_Setup { } {
   Debug "Show_Launcher_Setup"

   # Globals
   global FONT_NORMAL FONT_BOLD DEFAULT_PADDING RIDGE_BORDER \
      g_launcher_number g_launcher_changed \
      TKNET_LAUNCHER_SETUP_GEOMETRY

   # Create the setup toplevel as a transient from the main window
   if [winfo exists .launcher_setup] {destroy .launcher_setup}
   toplevel .launcher_setup
   wm title .launcher_setup "TkNet Launcher Setup"
   wm transient .launcher_setup .
   wm geometry .launcher_setup $TKNET_LAUNCHER_SETUP_GEOMETRY
   wm protocol .launcher_setup WM_DELETE_WINDOW Close_Launcher_Setup

   ###########################################################################
   # Create Menu Bar
   frame .launcher_setup.mbar -relief raised -bd 2
   pack .launcher_setup.mbar -side top -fill x

   menubutton .launcher_setup.mbar.file -text File -font $FONT_NORMAL \
      -underline 0 \
      -menu .launcher_setup.mbar.file.menu
   menubutton .launcher_setup.mbar.edit -text Edit -font $FONT_NORMAL \
      -underline 0 \
      -menu .launcher_setup.mbar.edit.menu
   pack .launcher_setup.mbar.file .launcher_setup.mbar.edit -side left

   menu .launcher_setup.mbar.file.menu -tearoff 0
      .launcher_setup.mbar.file.menu add command -label "Save" \
         -command { Save_Launcher_Setup } -font $FONT_NORMAL \
         -underline 0
      .launcher_setup.mbar.file.menu add command -label \
         "Set Defaults from Global" -command \
         { Source_Launcher_Setup 1 ; Show_Launcher_Setup ; \
         set g_setup_changed 1 } -font $FONT_NORMAL -underline 4
      .launcher_setup.mbar.file.menu add separator
      .launcher_setup.mbar.file.menu add command -label "Close" \
         -command { Close_Launcher_Setup } -font $FONT_NORMAL \
         -underline 0

   menu .launcher_setup.mbar.edit.menu -tearoff 0
      .launcher_setup.mbar.edit.menu add command -label "Add to end" \
         -command {New_Launcher_Item $g_launcher_number; \
         incr g_launcher_number; set g_launcher_changed 1} \
         -font $FONT_NORMAL -underline 0
      .launcher_setup.mbar.edit.menu add command -label \
         "Add before current" \
         -command {New_Launcher_Item $g_current_launcher; \
         incr g_launcher_number; set g_launcher_changed 1} \
         -font $FONT_NORMAL -underline 0
      .launcher_setup.mbar.edit.menu add separator
      .launcher_setup.mbar.edit.menu add command -label "Delete current" \
         -command {Delete_Launcher_Item; \
         set g_launcher_changed 1} \
         -font $FONT_NORMAL -underline 0

   tk_menuBar .launcher_setup.mbar .launcher_setup.mbar.file \
      .launcher_setup.mbar.edit

   ###############################################################
   # Create the instructions
   button .launcher_setup.label -text \
      "Enter the launcher items in the fields below.\n\
To obtain help click on this label." \
      -font $FONT_NORMAL -relief flat \
      -command "Mini_Help g_launcher_setup_text_help"
   frame .launcher_setup.dummy -width 10c -height 3m
   pack .launcher_setup.label .launcher_setup.dummy \
      -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING -side top -fill x

   ###############################################################
   # Create the column headers
   label .launcher_setup.column1 -text "Label" \
      -font $FONT_NORMAL
   place .launcher_setup.column1 -in .launcher_setup.label \
      -rely 1.0 -y 5 -x 80 
   label .launcher_setup.column2 -text "Bitmap/Pixmap" \
      -font $FONT_NORMAL
   place .launcher_setup.column2 -in .launcher_setup.label \
      -rely 1.0 -y 5 -x 230 
   label .launcher_setup.column3 -text "Command to Execute" \
      -font $FONT_NORMAL
   place .launcher_setup.column3 -in .launcher_setup.label \
      -rely 1.0 -y 5 -x 380

   ###############################################################
   # Create the main panel
   frame .launcher_setup.setup_fr -borderwidth $RIDGE_BORDER \
      -relief groove 
   pack .launcher_setup.setup_fr -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING -side top -expand true -fill both
   canvas .launcher_setup.setup_fr.c -yscrollcom \
      ".launcher_setup.setup_fr.sy set" -scrollregion \
      "0 0 600 0" -width 600 -highlightthickness 0
   pack [scrollbar .launcher_setup.setup_fr.sy -orient v \
      -com ".launcher_setup.setup_fr.c yview"] \
      -side right -fill y
   pack .launcher_setup.setup_fr.c -padx $DEFAULT_PADDING \
      -pady $DEFAULT_PADDING -side top -fill both \
      -expand true -exp 1

   ###############################################################
   # Create the individual settings
   set total $g_launcher_number
   set g_launcher_number 0
   for {set count 0} {$count < $total} {incr count} \
   {
      # Create a row for the item
      New_Launcher_Item $count
      incr g_launcher_number

      # Define globals
      global "g_launcher_label_$count" \
         "g_launcher_bitmap_$count" "g_launcher_command_$count"

      # Write launcher item
      set text [eval subst {\$g_launcher_label_$count}]
      .launcher_setup.setup_fr.c.$count.label insert end $text
      set text [eval subst {\$g_launcher_bitmap_$count}]
      .launcher_setup.setup_fr.c.$count.bitmap insert end $text
      set text [eval subst {\$g_launcher_command_$count}]
      .launcher_setup.setup_fr.c.$count.command insert end $text
   }


   frame .launcher_setup.button_frame -borderwidth $DEFAULT_PADDING
   pack .launcher_setup.button_frame -side bottom -fill x
   button .launcher_setup.button_frame.close -font $FONT_NORMAL \
       -text Close -command { Close_Launcher_Setup }
   pack .launcher_setup.button_frame.close
}

###############################################################
# Create a button, entry pair
# Number is the item to insert after
proc New_Launcher_Item { number } {
   Debug "New_Launcher_Item"

   # Globals
   global FONT_NORMAL DEFAULT_PADDING \
      LAUNCHER_ITEM_LENGTH \
      g_launcher_changed g_current_launcher \
      g_launcher_number

   # Work out the canvas height
   set canvas_height [expr $g_launcher_number * 40]
   .launcher_setup.setup_fr.c configure -scrollregion \
      "0 0 600 $canvas_height"

   # Create a label/textentry pair
   frame .launcher_setup.setup_fr.c.$g_launcher_number
   entry .launcher_setup.setup_fr.c.$g_launcher_number.label \
      -font $FONT_NORMAL \
      -width $LAUNCHER_ITEM_LENGTH
   bind .launcher_setup.setup_fr.c.$g_launcher_number.label \
      <KeyPress> "set g_launcher_changed 1"
   bind .launcher_setup.setup_fr.c.$g_launcher_number.label \
      <FocusIn> "set g_current_launcher $g_launcher_number"
   entry .launcher_setup.setup_fr.c.$g_launcher_number.bitmap \
      -font $FONT_NORMAL \
      -width $LAUNCHER_ITEM_LENGTH
   bind .launcher_setup.setup_fr.c.$g_launcher_number.bitmap \
      <KeyPress> "set g_launcher_changed 1"
   bind .launcher_setup.setup_fr.c.$g_launcher_number.bitmap \
      <FocusIn> "set g_current_launcher $g_launcher_number"
   entry .launcher_setup.setup_fr.c.$g_launcher_number.command \
      -font $FONT_NORMAL \
      -width $LAUNCHER_ITEM_LENGTH
   bind .launcher_setup.setup_fr.c.$g_launcher_number.command \
      <KeyPress> "set g_launcher_changed 1"
   bind .launcher_setup.setup_fr.c.$g_launcher_number.command \
      <FocusIn> "set g_current_launcher $g_launcher_number"
   pack .launcher_setup.setup_fr.c.$g_launcher_number.label \
      .launcher_setup.setup_fr.c.$g_launcher_number.bitmap \
      .launcher_setup.setup_fr.c.$g_launcher_number.command \
       -side left -padx $DEFAULT_PADDING -pady $DEFAULT_PADDING

   set offset [expr (35 * $g_launcher_number)]
   .launcher_setup.setup_fr.c create window 10 $offset \
      -window .launcher_setup.setup_fr.c.$g_launcher_number \
      -anchor nw

   # If number is less that the current total, user
   # has requested an insert.  Move all items below
   # number down one.
   if {$number < $g_launcher_number} {

      set last [expr $g_launcher_number -1]
      for {set count $last} {$count >= $number} {incr count -1} \
      {
         # Copy the text from the current item to the next item
         set next [expr $count + 1]
   
         # The label
         set text [.launcher_setup.setup_fr.c.$count.label get]
         .launcher_setup.setup_fr.c.$next.label delete 0 end
         .launcher_setup.setup_fr.c.$next.label insert end $text
   
         # The bitmap
         set text [.launcher_setup.setup_fr.c.$count.bitmap get]
         .launcher_setup.setup_fr.c.$next.bitmap delete 0 end
         .launcher_setup.setup_fr.c.$next.bitmap insert end $text
   
         # The command
         set text [.launcher_setup.setup_fr.c.$count.command get]
         .launcher_setup.setup_fr.c.$next.command delete 0 end
         .launcher_setup.setup_fr.c.$next.command insert end $text
      }

      # Blank the new entry
      .launcher_setup.setup_fr.c.$number.label delete 0 end
      .launcher_setup.setup_fr.c.$number.bitmap delete 0 end
      .launcher_setup.setup_fr.c.$number.command delete 0 end
   }
   # Do not increment option number - responsibility of caller
}

###############################################################
# Deletes the launcher item given by g_current_launcher
proc Delete_Launcher_Item {} {
   Debug "Delete_Launcher_Item"

   # Globals
   global g_launcher_changed g_current_launcher \
      g_launcher_number g_launcher_changed

   set last [expr $g_launcher_number - 1]
   for {set count $g_current_launcher} {$count < $last} \
      {incr count} \
   {
      # Copy the text from the next item to the current item
      set next [expr $count + 1]

      # Check for bounds
      if {$next > $last} {
         continue
      }

      # The label
      set text [.launcher_setup.setup_fr.c.$next.label get]
      .launcher_setup.setup_fr.c.$count.label delete 0 end
      .launcher_setup.setup_fr.c.$count.label insert end $text

      # The bitmap
      set text [.launcher_setup.setup_fr.c.$next.bitmap get]
      .launcher_setup.setup_fr.c.$count.bitmap delete 0 end
      .launcher_setup.setup_fr.c.$count.bitmap insert end $text

      # The command
      set text [.launcher_setup.setup_fr.c.$next.command get]
      .launcher_setup.setup_fr.c.$count.command delete 0 end
      .launcher_setup.setup_fr.c.$count.command insert end $text
   }

   # Now delete the last row
   destroy .launcher_setup.setup_fr.c.$last

   # And decrease the count
   incr g_launcher_number -1
   set g_launcher_changed 1
}

###############################################################
# The procedure for quitting the setup window
proc Close_Launcher_Setup {} {
   Debug "Close_Launcher_Setup"

   # Globals
   global g_launcher_changed

   # Check to see whether anything has changed.
   if {$g_launcher_changed == 1 && ![Question_Dialog .launcher_setup \
      "Do you wish to save changes?" "Save" "Reset Changes"]} {

      # Save requested
      Save_Launcher_Setup
   } else {

      # Re-read defaults from file
      Source_Launcher_Setup 0
      set g_launcher_changed 0
   }

   # Destroy the window
   destroy .launcher_setup 
}

###############################################################
# The procedure for saving the configuration
proc Save_Launcher_Setup {} {
   Debug "Save_Launcher_Setup"

   # Globals
   global g_launcher_number g_setup_file g_launcher_changed \
      TKNET_USER TKNET_GLOBAL_LAUNCHER_FILE \
      TKNET_LOCAL_LAUNCHER_FILE GREEN \
      gb_launcher_window

   # Save the setup to file
   # For each of the piars of items in the window,
   # ascertain the variable name being modified, then
   # write the output to a temporary file

   # If root save to global settings, else save to users
   # local ~/.tknet_setup file
   if {$TKNET_USER == "root" && [Question_Dialog \
      . "Where do you want to save lancher setup to?" \
      "Local" "Global"]} {
         set g_setup_file $TKNET_GLOBAL_LAUNCHER_FILE
   } else {
      set g_setup_file $TKNET_LOCAL_LAUNCHER_FILE
   }

   # Open the file
   set file_id [ open $g_setup_file w ]
   puts $file_id "## TkNet Launcher Information"
   puts $file_id " "

   # Write the number of items in the launcher
   puts $file_id "## Number of items"
   puts $file_id "global g_launcher_number"
   puts $file_id [join [list "set g_launcher_number" \
      "$g_launcher_number"]]

   # Write all information
   puts $file_id " "
   puts $file_id "## Launcher Items"
   for {set count 0} {$count < $g_launcher_number} \
      {incr count} \
   {
      # Write launcher item
      puts $file_id [join [list "global g_launcher_label_" \
         $count " g_launcher_bitmap_" $count \
         " g_launcher_command_" $count] ""]
      set text [.launcher_setup.setup_fr.c.$count.label get]
      puts $file_id [join [list "set g_launcher_label_" $count \
         " \"" $text \"] ""]
      set text [.launcher_setup.setup_fr.c.$count.bitmap get]
      puts $file_id [join [list "set g_launcher_bitmap_" $count \
         " \"" $text \"] ""]
      set text [.launcher_setup.setup_fr.c.$count.command get]
      puts $file_id [join [list "set g_launcher_command_" $count \
         " \"" $text \"] ""]
         puts $file_id " "
   }

   # Close file
   close $file_id

   # Display a dialog
   Set_Message "Launcher Items Saved to $g_setup_file" $GREEN
   set g_launcher_changed 0

   # Re-read from file
   Source_Launcher_Setup 0

   # Show the launcher again
   Show_Launcher $gb_launcher_window
}

###############################################################
# Show the setup panel 
proc Show_Script_Setup { } {
   Debug "Show_Script_Setup"

   # Globals
   global FONT_NORMAL FONT_BOLD DEFAULT_PADDING RIDGE_BORDER \
      g_script_number g_script_changed \
      TKNET_SCRIPT_SETUP_GEOMETRY

   # Create the setup toplevel as a transient from the main window
   if [winfo exists .script_setup] {destroy .script_setup}
   toplevel .script_setup
   wm title .script_setup "TkNet Script Setup"
   wm transient .script_setup .
   wm geometry .script_setup $TKNET_SCRIPT_SETUP_GEOMETRY
   wm protocol .script_setup WM_DELETE_WINDOW Close_Script_Setup

   ###########################################################################
   # Create Menu Bar
   frame .script_setup.mbar -relief raised -bd 2
   pack .script_setup.mbar -side top -fill x

   menubutton .script_setup.mbar.file -text File -font $FONT_NORMAL \
      -underline 0 \
      -menu .script_setup.mbar.file.menu
   menubutton .script_setup.mbar.edit -text Edit -font $FONT_NORMAL \
      -underline 0 \
      -menu .script_setup.mbar.edit.menu
   pack .script_setup.mbar.file .script_setup.mbar.edit -side left

   menu .script_setup.mbar.file.menu -tearoff 0
      .script_setup.mbar.file.menu add command -label "Save" \
         -command { Save_Script_Setup } -font $FONT_NORMAL \
         -underline 0
      .script_setup.mbar.file.menu add command -label \
         "Set Defaults from Global" -command \
         { Source_Script_Setup 1 ; Show_Script_Setup ; \
         set g_setup_changed 1 } -font $FONT_NORMAL -underline 4
      .script_setup.mbar.file.menu add separator
      .script_setup.mbar.file.menu add command -label "Close" \
         -command { Close_Script_Setup } -font $FONT_NORMAL \
         -underline 0

   menu .script_setup.mbar.edit.menu -tearoff 0
      .script_setup.mbar.edit.menu add command -label "Add to end" \
         -command {New_Script_Item $g_script_number; \
         incr g_script_number; set g_script_changed 1} \
         -font $FONT_NORMAL -underline 0
      .script_setup.mbar.edit.menu add command -label \
         "Add before current" \
         -command {New_Script_Item $g_current_script; \
         incr g_script_number; set g_script_changed 1} \
         -font $FONT_NORMAL -underline 0
      .script_setup.mbar.edit.menu add separator
      .script_setup.mbar.edit.menu add command -label "Delete current" \
         -command {Delete_Script_Item; \
         set g_script_changed 1} \
         -font $FONT_NORMAL -underline 0

   tk_menuBar .script_setup.mbar .script_setup.mbar.file \
      .script_setup.mbar.edit

   ###############################################################
   # Create the instructions
   button .script_setup.label -text \
      "Enter the connection scripts in the fields below.\n\
To obtain help click on this label." \
      -font $FONT_NORMAL -relief flat \
      -command "Mini_Help g_script_setup_text_help"
   frame .script_setup.dummy -width 10c -height 3m
   pack .script_setup.label .script_setup.dummy \
      -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING -side top -fill x

   ###############################################################
   # Create the column headers
   label .script_setup.column1 -text "Script Label" \
      -font $FONT_NORMAL
   place .script_setup.column1 -in .script_setup.label \
      -rely 1.0 -y 5 -x 40 
   label .script_setup.column2 -text "Connect Command" \
      -font $FONT_NORMAL
   place .script_setup.column2 -in .script_setup.label \
      -rely 1.0 -y 5 -x 170 
   label .script_setup.column3 -text "Disconnect Command" \
      -font $FONT_NORMAL
   place .script_setup.column3 -in .script_setup.label \
      -rely 1.0 -y 5 -x 360 
   label .script_setup.column4 -text "Default" \
      -font $FONT_NORMAL
   place .script_setup.column4 -in .script_setup.label \
      -rely 1.0 -y 5 -x 530

   ###############################################################
   # Create the main panel
   frame .script_setup.setup_fr -borderwidth $RIDGE_BORDER \
      -relief groove 
   pack .script_setup.setup_fr -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING -side top -expand true -fill both
   canvas .script_setup.setup_fr.c -yscrollcom \
      ".script_setup.setup_fr.sy set" -scrollregion \
      "0 0 600 0" -width 600 -highlightthickness 0
   pack [scrollbar .script_setup.setup_fr.sy -orient v \
      -com ".script_setup.setup_fr.c yview"] \
      -side right -fill y
   pack .script_setup.setup_fr.c -padx $DEFAULT_PADDING \
      -pady $DEFAULT_PADDING -side top -fill both \
      -expand true -exp 1

   ###############################################################
   # Create the individual settings
   set total $g_script_number
   set g_script_number 0
   for {set count 0} {$count < $total} {incr count} \
   {
      # Create a row for the item
      New_Script_Item $count
      incr g_script_number

      # Define globals
      global "g_script_label_$count" \
         "g_script_script_$count" "g_script_disconnect_$count"

      # Write Script item
      set text [eval subst {\$g_script_label_$count}]
      .script_setup.setup_fr.c.$count.label insert end $text
      set text [eval subst {\$g_script_script_$count}]
      .script_setup.setup_fr.c.$count.script insert end $text
      set text [eval subst {\$g_script_disconnect_$count}]
      .script_setup.setup_fr.c.$count.disconnect insert end $text
   }

   frame .script_setup.button_frame -borderwidth $DEFAULT_PADDING
   pack .script_setup.button_frame -side bottom -fill x
   button .script_setup.button_frame.close -font $FONT_NORMAL \
       -text Close -command { Close_Script_Setup }
   pack .script_setup.button_frame.close
}

###############################################################
# Create a button, entry pair
# Number is the item to insert after
proc New_Script_Item { number } {
   Debug "New_Script_Item"

   # Globals
   global FONT_NORMAL DEFAULT_PADDING \
      SCRIPT_ITEM_LENGTH SCRIPT_LABEL_LENGTH \
      g_script_changed g_current_script \
      g_script_number gi_default_script

   # Work out the canvas height
   set canvas_height [expr $g_script_number * 40]
   .script_setup.setup_fr.c configure -scrollregion \
      "0 0 600 $canvas_height"

   # Create a label/textentry pair
   frame .script_setup.setup_fr.c.$g_script_number
   entry .script_setup.setup_fr.c.$g_script_number.label \
      -font $FONT_NORMAL \
      -width $SCRIPT_LABEL_LENGTH
   bind .script_setup.setup_fr.c.$g_script_number.label \
      <KeyPress> "set g_script_changed 1"
   bind .script_setup.setup_fr.c.$g_script_number.label \
      <FocusIn> "set g_current_script $g_script_number"
   entry .script_setup.setup_fr.c.$g_script_number.script \
      -font $FONT_NORMAL \
      -width $SCRIPT_ITEM_LENGTH
   bind .script_setup.setup_fr.c.$g_script_number.script \
      <KeyPress> "set g_script_changed 1"
   bind .script_setup.setup_fr.c.$g_script_number.script \
      <FocusIn> "set g_current_script $g_script_number"
   entry .script_setup.setup_fr.c.$g_script_number.disconnect \
      -font $FONT_NORMAL \
      -width $SCRIPT_ITEM_LENGTH
   bind .script_setup.setup_fr.c.$g_script_number.disconnect \
      <KeyPress> "set g_script_changed 1"
   bind .script_setup.setup_fr.c.$g_script_number.disconnect \
      <FocusIn> "set g_current_script $g_script_number"
   radiobutton .script_setup.setup_fr.c.$g_script_number.command \
      -font $FONT_NORMAL \
      -width $SCRIPT_ITEM_LENGTH -variable gi_default_script \
      -value $number -anchor w
   bind .script_setup.setup_fr.c.$g_script_number.command \
      <ButtonPress> "set g_script_changed 1"
   bind .script_setup.setup_fr.c.$g_script_number.command \
      <FocusIn> "set g_current_script $g_script_number"
   pack .script_setup.setup_fr.c.$g_script_number.label \
      .script_setup.setup_fr.c.$g_script_number.script \
      .script_setup.setup_fr.c.$g_script_number.disconnect \
      .script_setup.setup_fr.c.$g_script_number.command \
       -side left -padx $DEFAULT_PADDING -pady $DEFAULT_PADDING

   set offset [expr (35 * $g_script_number)]
   .script_setup.setup_fr.c create window 10 $offset \
      -window .script_setup.setup_fr.c.$g_script_number \
      -anchor nw

   # If number is less that the current total, user
   # has requested an insert.  Move all items below
   # number down one.
   if {$number < $g_script_number} {

      set last [expr $g_script_number -1]
      for {set count $last} {$count >= $number} {incr count -1} \
      {
         # Copy the text from the current item to the next item
         set next [expr $count + 1]
   
         # The label
         set text [.script_setup.setup_fr.c.$count.label get]
         .script_setup.setup_fr.c.$next.label delete 0 end
         .script_setup.setup_fr.c.$next.label insert end $text
   
         # The script
         set text [.script_setup.setup_fr.c.$count.script get]
         .script_setup.setup_fr.c.$next.script delete 0 end
         .script_setup.setup_fr.c.$next.script insert end $text
   
         # The disconnect script
         set text [.script_setup.setup_fr.c.$count.disconnect get]
         .script_setup.setup_fr.c.$next.disconnect delete 0 end
         .script_setup.setup_fr.c.$next.disconnect insert end $text
   
         # The default
         if {$gi_default_script == $count} {
            incr gi_default_script
         }
      }

      # Blank the new entry
      .script_setup.setup_fr.c.$number.label delete 0 end
      .script_setup.setup_fr.c.$number.script delete 0 end
      .script_setup.setup_fr.c.$number.disconnect delete 0 end
   }
   # Do not increment option number - responsibility of caller
}

###############################################################
# Deletes the script item given by g_current_script
proc Delete_Script_Item {} {
   Debug "Delete_Script_Item"

   # Globals
   global g_script_changed g_current_script \
      g_script_number g_script_changed

   set last [expr $g_script_number - 1]
   for {set count $g_current_script} {$count < $last} \
      {incr count} \
   {
      # Copy the text from the next item to the current item
      set next [expr $count + 1]

      # Check for bounds
      if {$next > $last} {
         continue
      }

      # The label
      set text [.script_setup.setup_fr.c.$next.label get]
      .script_setup.setup_fr.c.$count.label delete 0 end
      .script_setup.setup_fr.c.$count.label insert end $text

      # The script
      set text [.script_setup.setup_fr.c.$next.script get]
      .script_setup.setup_fr.c.$count.script delete 0 end
      .script_setup.setup_fr.c.$count.script insert end $text

      # The disconnect script
      set text [.script_setup.setup_fr.c.$next.disconnect get]
      .script_setup.setup_fr.c.$count.disconnect delete 0 end
      .script_setup.setup_fr.c.$count.disconnect insert end $text

      # The default
      if {$gi_default_script == $count} {
         incr gi_default_script -1
      }
   }

   # Now delete the last row
   destroy .script_setup.setup_fr.c.$last

   # And decrease the count
   incr g_script_number -1
   set g_script_changed 1
}

###############################################################
# The procedure for quitting the setup window
proc Close_Script_Setup {} {
   Debug "Close_Script_Setup"

   # Globals
   global g_script_changed

   # Check to see whether anything has changed.
   if {$g_script_changed == 1 && ![Question_Dialog .script_setup \
      "Do you wish to save changes?" "Save" "Reset Changes"]} {

      # Save requested
      Save_Script_Setup
   } else {

      # Re-read defaults from file
      Source_Script_Setup 0
      set g_script_changed 0
   }

   # Destroy the window
   destroy .script_setup 
}

###############################################################
# The procedure for saving the configuration
proc Save_Script_Setup {} {
   Debug "Save_Script_Setup"

   # Globals
   global g_script_number g_setup_file g_script_changed \
      TKNET_USER TKNET_GLOBAL_SCRIPT_FILE \
      TKNET_LOCAL_SCRIPT_FILE GREEN \
      gb_script_window gi_default_script

   # Save the setup to file
   # For each of the piars of items in the window,
   # ascertain the variable name being modified, then
   # write the output to a temporary file

   # If root save to global settings, else save to users
   # local ~/.tknet_setup file
   if {$TKNET_USER == "root" && [Question_Dialog \
      . "Where do you want to save script setup to?" \
      "Local" "Global"]} {
         set g_setup_file $TKNET_GLOBAL_SCRIPT_FILE
   } else {
      set g_setup_file $TKNET_LOCAL_SCRIPT_FILE
   }

   # Open the file
   set file_id [ open $g_setup_file w ]
   puts $file_id "## TkNet Script Information"
   puts $file_id " "

   # Write the number of items in the script
   puts $file_id "## Number of items"
   puts $file_id "global g_script_number"
   puts $file_id [join [list "set g_script_number" \
      "$g_script_number"]]
   puts $file_id "global gi_default_script"
   puts $file_id [join [list "set gi_default_script" \
      "$gi_default_script"]]

   # Write all information
   puts $file_id " "
   puts $file_id "## Script Items"
   for {set count 0} {$count < $g_script_number} \
      {incr count} \
   {
      # Write Script item
      puts $file_id [join [list "global g_script_label_" \
         $count " g_script_script_" $count \
         " g_script_disconnect_" $count] ""]
      set text [.script_setup.setup_fr.c.$count.label get]
      puts $file_id [join [list "set g_script_label_" $count \
         " \"" $text \"] ""]
      set text [.script_setup.setup_fr.c.$count.script get]
      puts $file_id [join [list "set g_script_script_" $count \
         " \"" $text \"] ""]
      set text [.script_setup.setup_fr.c.$count.disconnect get]
      puts $file_id [join [list "set g_script_disconnect_" $count \
         " \"" $text \"] ""]
      puts $file_id " "
   }

   # Close file
   close $file_id

   # Display a dialog
   Set_Message "Script Items Saved to $g_setup_file" $GREEN
   set g_script_changed 0

   # Re-read from file
   Source_Script_Setup 0

   # Show the script again
   Show_Script_Setup

   # Re-create both menus
   Create_File_Menu
   Create_Popup_Menu
}

###############################################################
# The procedure for creating images
proc Create_Image { file } {
   Debug "Create_Image"

   # This procedure attempts to create an image from
   # the data specified in the named file.  If the 
   # file is non-existant, or the image type is not
   # supported, -1 is returned and a dialogue is
   # displayed.

   if [file exists $file] {

      # Create the image
      set name [join [list $file "_image"] ""]
      if [catch {image create bitmap $name -file $file} text] {
         # Not a bitmap, try pixmap
         if [catch {image create pixmap $name -file $file} text] {
            # Not a pixmap, try gif
            if [catch {image create photo $name -file $file} text] {
               # Give up!
               set error [join [list "Image" $file "has an unrecognised \
type!\n See help on images for more information."]]
               Info_Dialog . $error
               return -1
            }
         }
      }
      return $name
   } else {
      set text [join [list "File" $file "\ndoes not exist!"]]
      Info_Dialog . $text
      return -1
   }
}

###############################################################
# The procedure for adding and subtracting times
proc AddSub_Time { time1 time2 addsub format } {
   Debug "AddSub_Time"

   # Time2 is the larger time (more recent).   
   # Addsub must be 1 (Add) or -1 (Subtract)
   # Format must be 1 (Seconds) or 2 (Time)

   # Input formats must be as returned from 
   # Get_Current_Time, e.g. DDD/HH:MM:SS.

   # Oh, this routine will not handle years, nor
   # will it handle -ve differences at the moment.

   set secs1 [Time_To_Secs $time1]
   set secs2 [Time_To_Secs $time2]

   # If the first time is zero return null values
   if {$secs1 == 0} {
      # Return a null value
      set secs3 0
   } else {
      # Perform calculation
      set secs3 [expr $secs2 + ( $addsub * $secs1 )]
   }

   # Do output conversion
   if {$format == 1} {
      # Return as seconds
      return $secs3
   } else {
      # Return formatted
      return [Secs_To_Time $secs3]
   }
}

###############################################################
# The procedure for finding the current time
proc Get_Current_Time {} {
   Debug "Get_Current_Time"

   # Format is DDD/HH:MM:SS
   return [exec date +%j/%H:%M:%S]
}

###############################################################
# The procedure for converting time format to secs format
proc Time_To_Secs {time} {
   Debug "Time_To_Secs"

   # Format is DDD/HH:MM:SS
   set count [scan $time "%d/%d:%d:%d" day hour minute second]
   if {$count != 4} {return 0}
   return [expr $second + (60 * ($minute + (60 * ($hour + \
      (24 * $day)))))]
}

###############################################################
# The procedure for converting secs format to time format
proc Secs_To_Time {secs} {
   Debug "Secs_To_Time"

   # Format is DDD/HH:MM:SS
   set day [expr $secs / [expr 60 * 60 * 24]]
   set secs [expr $secs - [expr $day * 60 * 60 * 24]]
   set hour [expr $secs / [expr 60 * 60]]
   set secs [expr $secs - [expr $hour * 60 * 60]]
   set min [expr $secs / 60]
   set secs [expr $secs - [expr $min * 60]]

   # Return formatted string
   return [format "%03d/%02d:%02d:%02d" $day $hour $min $secs]
}

###############################################################
# The procedure for creating and displaying the launcher
# Argument must be 0=Embed 1=External
proc Show_Launcher { type } {
   Debug "Show_Launcher"

   # Globals
   global g_launcher_number gi_launcher_button_style \
      LAUNCHER_FONT RIDGE_BORDER LAUNCHER_COLOUR \
      TKNET_LAUNCHER_GEOMETRY gb_launcher_vertical \
      GREEN

   # If the window exists, destroy it
   if [winfo exists .launcher_window] {
      destroy .launcher_window
      wm geometry . ""
      update
   }

   # Manage window
   if {$type == 0} {

      # Create the window from the options specified
      frame .launcher_window -borderwidth $RIDGE_BORDER \
         -relief flat
      pack .launcher_window -after .mbar -side top -fill x

   } else {
   
      # Create as new window
      toplevel .launcher_window
      wm title .launcher_window "Launcher"
      wm geometry .launcher_window $TKNET_LAUNCHER_GEOMETRY
   }

   # Bind the options menu
   bind .launcher_window <Button-3> {Popup_Menu \
      .launcher_window .mbar.launcher.menu}

   for {set count 0} {$count < $g_launcher_number} \
      {incr count} \
   {
      # Get the current information
      global g_launcher_label_$count \
         g_launcher_bitmap_$count \
         g_launcher_command_$count
      set label [eval subst {\$g_launcher_label_$count}]
      set bitmap [eval subst {\$g_launcher_bitmap_$count}]
      set command [eval subst {\$g_launcher_command_$count}]

      set cmd [subst -nocommands {eval exec $command &; \
         Set_Message \"Started $label successfully.\" $GREEN;}]
      # Info_Dialog . $cmd

      if {$gi_launcher_button_style == 1} {

         # Labels are specified
         button .launcher_window.$count \
            -text $label -font $LAUNCHER_FONT \
            -background $LAUNCHER_COLOUR -command $cmd

      } elseif {$gi_launcher_button_style == 2} {

         # Bitmaps are specified
         set image [Create_Image $bitmap]
         if {$image == -1} {
            # Display label instead
            button .launcher_window.$count \
               -text $label -font $LAUNCHER_FONT \
               -background $LAUNCHER_COLOUR \
               -command $cmd
         } else {
            button .launcher_window.$count \
               -image $image -command $cmd
         }
      } else {

         # Display both bitmaps and images
         set image [Create_Image $bitmap]
         if {$image == -1} {
            # Display label instead
            button .launcher_window.$count \
               -text $label -font $LAUNCHER_FONT \
               -background $LAUNCHER_COLOUR \
               -command $cmd
         } else {
            frame .launcher_window.$count -relief raised \
               -borderwidth $RIDGE_BORDER -highlightthickness 0
            button .launcher_window.$count.i -command $cmd \
               -image $image -relief flat
            button .launcher_window.$count.l -command $cmd \
               -text $label -font $LAUNCHER_FONT -relief flat
            pack .launcher_window.$count.i \
               -side top -fill both -padx 0 -pady 0
            pack .launcher_window.$count.l \
               -side bottom -fill both -padx 0 -pady 0
            # Bind button down and up events in each
            # button to invoke the corresponding events
            # in the other
            bind .launcher_window.$count.l <ButtonPress> " \
               eval .launcher_window.$count configure -relief \
                  sunken"
            bind .launcher_window.$count.l <ButtonRelease> " \
               eval .launcher_window.$count configure -relief \
                  raised"
            bind .launcher_window.$count.i <ButtonPress> " \
               eval .launcher_window.$count configure -relief \
                  sunken"
            bind .launcher_window.$count.i <ButtonRelease> " \
               eval .launcher_window.$count configure -relief \
                  raised"
         }
      }

      # Pack buttons according to the orientation
      if {$type != 0 && $gb_launcher_vertical == 1} {
         pack .launcher_window.$count -side top -fill both \
            -padx 0 -pady 0
      } else {
         pack .launcher_window.$count -side left -fill both \
            -padx 0 -pady 0
      }
   }

   # Ascertain the 
}

###############################################################
# Show the mini-help panel
proc Mini_Help {text} {
   Debug "Mini_Help"

   # Globals
   global $text RIDGE_BORDER DEFAULT_PADDING FONT_NORMAL \
      gt_mini_help TKNET_HELP_GEOMETRY
   set local_text [eval subst {\$$text}]

   WatchCursor
   # Test for window
   if ![winfo exists .mini_help_window] {
   
      # Create the mini-help screen
      toplevel .mini_help_window
      wm title .mini_help_window "Help"
      wm transient .mini_help_window .
      wm geometry .mini_help_window $TKNET_HELP_GEOMETRY

      ###############################################################
      # Create the log panel
      frame .mini_help_window.fr -borderwidth $RIDGE_BORDER \
         -relief groove
      pack .mini_help_window.fr -padx $DEFAULT_PADDING -pady \
         $DEFAULT_PADDING -side top -expand true -fill both
      set gt_mini_help [ScrolledText \
         .mini_help_window.fr.help 80 20 0]
      pack .mini_help_window.fr.help -side top -anchor w
   
      ###############################################################
      # Create the buttons below the frame
      frame .mini_help_window.button_frame -borderwidth $DEFAULT_PADDING
      pack .mini_help_window.button_frame -side bottom -fill x
      button .mini_help_window.button_frame.close -font $FONT_NORMAL \
          -text Close -command { destroy .mini_help_window }
      pack .mini_help_window.button_frame.close

   }

   # Check for minimised state and raise it
   wm deiconify .mini_help_window
   raise .mini_help_window
   
   # Put the help text in the window
   $gt_mini_help configure -state normal
   $gt_mini_help delete 1.0 end
   $gt_mini_help insert end $local_text
   Parse_Help
   $gt_mini_help configure -state disabled -font \
      $FONT_NORMAL

   NormalCursor
}

###############################################################
# The procedure for finding tags in the help
proc Parse_Help {} {
   Debug "Parse Help"

   # Globals
   global gt_mini_help search FONT_BOLD FONT_ITALIC \
      FIXED_FONT

   # Format list
   set format_list [list title bold italic fixed]

   # Keyworks and formatting
   set title "title"
   set title_format "-font $FONT_BOLD -underline True -justify center"
   set bold  "b"
   set bold_format "-font $FONT_BOLD"
   set italic "i"
   set italic_format "-font $FONT_ITALIC"
   set fixed "pre"
   set fixed_format "-font $FIXED_FONT"

   # Loop round the declared types
   foreach type $format_list {

      # Start at the beginning
      set location    1.0
      while {$location != ""} {
         set start [eval $gt_mini_help search -nocase "<$$type>" \
            $location end]
         if {$start != ""} {
            set location [eval $gt_mini_help search -nocase </$$type> \
               $start end]
            $gt_mini_help tag add $type $start $location
         } else {set location ""}
      }
      # DO FORMATTING
      set format [eval subst {\$[join [list $type _format] ""]}]
      eval $gt_mini_help tag configure $type $format

      # Delete codes and format headers
      set start 1.0
      set end 1.0
      while {$start != "" || $end != ""} {
         set start [eval $gt_mini_help search -nocase "<$$type>" \
            $start end]
         if {$start != ""} {
            $gt_mini_help delete $start "$start +1c wordend +1c"}
         set end [eval $gt_mini_help search -nocase "</$$type>" \
            $end end]
         if {$end != ""} {
            $gt_mini_help delete $end "$end +2c wordend +1c"}
      }
   }
}

###############################################################
# The procedure for quitting the setup window
proc Close_Setup_Window {} {
   Debug "Close_Setup_Window"

   # Globals
   global g_setup_changed

   # Check to see whether anything has changed.
   if {$g_setup_changed == 1 && ![Question_Dialog .setup_window \
      "Do you wish to save changes?" "Save" "Reset Changes"]} {

      # Save requested
      Save_Setup
   } else {

      # Re-read defaults from file
      Source_Setup 0
      set g_setup_changed 0
   }

   # Destroy the window
   destroy .setup_window 
}

###############################################################
# The procedure for saving the configuration
proc Save_Setup {} {
   Debug "Save_Setup"

   # Globals
   global g_option_number g_setup_file g_setup_changed \
      TKNET_USER TKNET_GLOBAL_SETUP_FILE \
      TKNET_LOCAL_SETUP_FILE GREEN \
      gls_setup_list

   # Save the setup to file
   # For each of the piars of items in the window,
   # ascertain the variable name being modified, then
   # write the output to a temporary file

   # If root save to global settings, else save to users
   # local ~/.tknet_setup file
   if {$TKNET_USER == "root" && [Question_Dialog \
      . "Where do you want to save setup to?" \
      "Local" "Global"]} {
         set g_setup_file $TKNET_GLOBAL_SETUP_FILE
   } else {
      set g_setup_file $TKNET_LOCAL_SETUP_FILE
   }

   # Open the file
   set file_id [ open $g_setup_file w ]
   puts $file_id "## TkNet Setup Information"

   # Write all information   
   foreach option $gls_setup_list {

      # Write 
      global $option
      puts $file_id [join [list "set" "$option" \
        \"[eval subst {\$$option}]"]]
   }

   # Close file
   close $file_id

   # Display a dialog
   Set_Message "Setup Saved to $g_setup_file" $GREEN
   set g_setup_changed 0
}

###############################################################
# The procedure for saving the configuration options
proc Save_Options {} {
   Debug "Save_Options"

   # Globals
   global g_option_number g_option_file \
      TKNET_USER TKNET_GLOBAL_OPTIONS_FILE \
      TKNET_LOCAL_OPTIONS_FILE GREEN \
      gls_options_list gls_geometry_list 

   # Save the setup to file
   # For each of the piars of items in the window,
   # ascertain the variable name being modified, then
   # write the output to a temporary file

   # If root save to global settings, else save to users
   # local ~/.tknet_preferences file
   if {$TKNET_USER == "root" && [Question_Dialog \
      . "Where do you want to save options to?" \
      "Local" "Global"]} {
         set g_setup_file $TKNET_GLOBAL_OPTIONS_FILE
   } else {
      set g_setup_file $TKNET_LOCAL_OPTIONS_FILE
   }

   # Open the file
   set file_id [ open $g_setup_file w ]
   puts $file_id "## TkNet Options Information"

   foreach option $gls_options_list {

      # Write 
      global $option
      puts $file_id [join [list "set" "$option" \
        \"[eval subst {\$$option}]"]]
   }

   if {$gb_save_positions == 1} {

      # Get the current geometry information
      Get_Current_Geometry

      puts $file_id " "
      puts $file_id "## TkNet Geometry Information"
      foreach option $gls_geometry_list {

         # Write geometry info
         global $option
         puts $file_id [join [list "set" "$option" \
           \"[eval subst {\$$option}]"]]
      }
   }

   # Close file
   close $file_id

   # Display a dialog
   Set_Message "Options Saved to $g_setup_file" $GREEN
}

###############################################################
# The procedure for getting the geometry of currently
# displayed windows
proc Get_Current_Geometry {} {
   Debug "Get_Current_Geometry"

   # Globals
   global TKNET_GEOMETRY \
      TKNET_LOG_GEOMETRY TKNET_SETUP_GEOMETRY \
      TKNET_LAUNCHER_SETUP_GEOMETRY \
      TKNET_MAIL_GEOMETRY TKNET_LAUNCHER_GEOMETRY \
      TKNET_HELP_GEOMETRY TKNET_SCRIPT_SETUP_GEOMETRY

   # Get the following information only
   # if window currently mapped
   update 

   if [winfo exists .] {
      set TKNET_GEOMETRY [Get_Geometry .]
   }
   if [winfo exists .log_window] {
      set TKNET_LOG_GEOMETRY [Get_Geometry .log_window]
   }
   if [winfo exists .launcher_setup] {
      set TKNET_LAUNCHER_SETUP_GEOMETRY [Get_Geometry \
         .launcher_setup]
   }
   if [winfo exists .launcher_window] {
      set TKNET_LAUNCHER_GEOMETRY [Get_Geometry \
         .launcher_window]
   }
   if [winfo exists .setup_window] {
      set TKNET_SETUP_GEOMETRY [Get_Geometry .setup_window]
   }
   if [winfo exists .mail_window] {
      set TKNET_MAIL_GEOMETRY [Get_Geometry .mail_window]
   }
   if [winfo exists .mini_help_window] {
      set TKNET_HELP_GEOMETRY [Get_Geometry \
         .mini_help_window]
   }
   if [winfo exists .script_setup] {
      set TKNET_SCRIPT_SETUP_GEOMETRY [Get_Geometry \
         .script_setup]
   }
}

###############################################################
# Get the window position
proc Get_Geometry { window } {
   Debug "Get_Geometry"

   set y [winfo y $window]
   if {$y < 0} {set y 0}
   set x [winfo x $window]
   if {$x < 0} {set x 0}

   # Return the geometry for the window
   return "+$x+$y"
}

###############################################################
# Display a blocking information dialog
proc Info_Dialog { parent string } {
   Debug "Info_Dialog"

   # Globals
   global FONT_NORMAL RIDGE_BORDER DEFAULT_PADDING \
      BITMAP_HEIGHT BITMAP_WIDTH

   # Display the string to the user
   if [winfo exists .dialog] return
   toplevel .dialog
   wm title .dialog "Network Manager"
   wm transient .dialog .
#   wm resizable .dialog 0 0 
   grab current .dialog

   ###############################################################
   # Create the message
   frame .dialog.fr -borderwidth $RIDGE_BORDER -relief groove
   pack .dialog.fr -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING -side top -expand true -fill both
   label .dialog.fr.bitmap -bitmap info -height \
      $BITMAP_HEIGHT -width $BITMAP_WIDTH
   label .dialog.fr.message -text $string \
      -font $FONT_NORMAL
   pack .dialog.fr.bitmap .dialog.fr.message -side left \
      -anchor w

   ###############################################################
   # Create the buttons below the frame
   frame .dialog.button_frame -borderwidth $DEFAULT_PADDING
   pack .dialog.button_frame -side bottom -fill x
   button .dialog.button_frame.dismiss -font $FONT_NORMAL \
       -text Dismiss -command { destroy .dialog }
   pack .dialog.button_frame.dismiss

   
   ###############################################################
   # Bind return and space to dismiss
   bind . <Return> {destroy .dialog}
   bind . <space> {destroy .dialog}

   ###############################################################
   # Centre the dialog on the parent (was widget $parent)
   Centre_Dialog .dialog

   ###############################################################
   # Wait for the button to be pressed
   tkwait window .dialog
}

###############################################################
# Display a blocking question dialog
proc Question_Dialog { parent string button1 button2 } {
   Debug "Question_Dialog"

   # Globals
   global FONT_NORMAL RIDGE_BORDER DEFAULT_PADDING \
      BITMAP_HEIGHT BITMAP_WIDTH g_status

   # Initialise return status
   set g_status -1

   # Display the string to the user
   if [winfo exists .dialog] {return $g_status}
   toplevel .dialog
   wm title .dialog "Network Manager"
   wm transient .dialog .
#   wm resizable .dialog 0 0 
   grab current .dialog

   ###############################################################
   # Create the message
   frame .dialog.fr -borderwidth $RIDGE_BORDER -relief groove
   pack .dialog.fr -padx $DEFAULT_PADDING -pady \
      $DEFAULT_PADDING -side top -expand true -fill both
   label .dialog.fr.bitmap -bitmap questhead -height \
      $BITMAP_HEIGHT -width $BITMAP_WIDTH
   label .dialog.fr.message -text $string \
      -font $FONT_NORMAL
   pack .dialog.fr.bitmap .dialog.fr.message -side left \
      -anchor w

   ###############################################################
   # Create the buttons below the frame
   frame .dialog.button_frame -borderwidth $DEFAULT_PADDING
   pack .dialog.button_frame -side bottom -fill x
   button .dialog.button_frame.ok -font $FONT_NORMAL \
      -text $button1 -command {destroy .dialog; set g_status 0}
   button .dialog.button_frame.cancel -font $FONT_NORMAL \
      -text $button2 -command {destroy .dialog; set g_status 1}
   pack .dialog.button_frame.ok .dialog.button_frame.cancel \
      -side right

   ###############################################################
   # Centre the dialog on the parent
   Centre_Dialog .dialog

   ###############################################################
   # Wait for the button to be pressed
   tkwait variable g_status
   return $g_status
}

###############################################################
# Centre a window on the screen (or parent)
proc Centre_Dialog {window {position ""} {parent ""}} {
   Debug "Centre_Dialog"

   # Withdraw dialog and update all windows
   wm withdraw $window
   update idletasks
   set win_width [winfo reqwidth $window]
   set win_height [winfo reqheight $window]

   # Read the positioning argument (pointer, widget, default)
   switch -glob -- $position {
      p* {
         # place at POINTER (centered is $a == center)
         wm geometry $window +[expr \
            [winfo pointerx $window]-$win_width \
            /2]+[expr [winfo pointery $window]-\
            $win_height/2]
      }
      w* {
         # center about WIDGET $parent
         wm geometry $window +[expr [winfo rootx $parent]+ \
            ([winfo width $parent]-[winfo width $window])/2]+[expr \
            [winfo rooty $parent]+([winfo height \
            $parent]-[winfo height $window])/2]
      }
      default {
         wm geometry $window +[expr ([winfo screenwidth \
            $window]-[winfo reqwidth $window]) \
            / 2]+[expr ([winfo screenheight $window]- \
            [winfo reqheight $window]) / 2]
      }
   }

   # Now show the window
   wm deiconify $window
}

###############################################################
# Create a scrolled text widget
proc ScrolledText { f width height horiz } {
   Debug "ScrolledText"

   # Global 
   global FIXED_FONT_SMALL

   frame $f
   # The setgrid setting allows the window to be resized.
   text $f.text -width $width -height $height \
      -setgrid true  -width $width -wrap word \
      -yscrollcommand [list $f.yscroll set] \
      -font $FIXED_FONT_SMALL
   if {$horiz == 1} {
      $f.text configure -xscrollcommand [list \
         $f.xscroll set] -wrap none 
      scrollbar $f.xscroll -orient horizontal \
         -command [list $f.text xview]
      pack $f.xscroll -side bottom -fill x
   }
   scrollbar $f.yscroll -orient vertical \
      -command [list $f.text yview]
   pack $f.yscroll -side right -fill y

   # The fill and expand are needed when resizing.
   pack $f.text -side left -fill both -expand true
   pack $f -side top -fill both -expand true
   return $f.text
}

###############################################################
# NEXT THE INTERFACE CODE, I.E. THE STUFF THAT BUILDS THE
# INTERFACE
###############################################################

# Source the setup files
Source_Setup 0
Source_Options 0
Source_Launcher_Setup 0
Source_Script_Setup 0

# And the help file
if [file exists $TKNET_HELP_FILE] {
   source $TKNET_HELP_FILE
}

# Delete the log file if one exists
if [file exists $g_log_file] {
   catch {exec rm -f $g_log_file} text 
}

# Create a new file and modify permissions
catch {eval exec touch $g_log_file} text
catch {eval exec chmod 777 $g_log_file} text

###############################################################
# Check for version
if {$tcl_version < 7.4 || $tk_version < 4.0} {
   # Out-of-date wish binaries...
   set version_info [join [list Found : Tcl $tcl_version, Tk \
      $tk_version. \n\n You must have at least \n Tcl 7.4 and \
      Tk 4.0! \n\n TkNet will now exit.]]
   exit
}

###############################################################
# Create a dialogue
wm title . "TkNet - Network Manager"
wm iconname . "Net Down"
wm geometry . $TKNET_GEOMETRY
wm minsize . 40 5
#wm iconbitmap . @/usr/include/X11/pixmaps/netdn.xpm
#wm resizable . 0 0 
wm protocol . WM_DELETE_WINDOW Quit
if {$gb_startup_iconified == 1} {wm iconify .}

###########################################################################
# Create Menu Bar
frame .mbar -relief raised -bd 2
#frame .dummy -width 10c -height 5m
pack .mbar -side top -fill x

menubutton .mbar.file -text File -font $FONT_NORMAL -underline 0 \
   -menu .mbar.file.menu
menubutton .mbar.windows -text Windows -font $FONT_NORMAL \
   -underline 0 -menu .mbar.windows.menu
menubutton .mbar.options -text Options -font $FONT_NORMAL \
   -underline 0 -menu .mbar.options.menu
menubutton .mbar.launcher -text Launcher -font $FONT_NORMAL \
   -underline 0 -menu .mbar.launcher.menu
menubutton .mbar.help -text Help -font $FONT_NORMAL -underline 0 \
   -menu .mbar.help.menu
pack .mbar.file .mbar.windows .mbar.options .mbar.launcher \
   -side left
pack .mbar.help -side right

# Create the file menu from stored script items
Create_File_Menu

menu .mbar.windows.menu -tearoff 0
   .mbar.windows.menu add command -label "View Log ..." -command \
      { Show_Log } -font $FONT_NORMAL -underline 0
   .mbar.windows.menu add command -label "Mail ..." -command \
      { Show_Mail } -font $FONT_NORMAL -underline 0

menu .mbar.options.menu -tearoff 0
   .mbar.options.menu add command -label "Setup ..." -command \
      { Show_Setup } -font $FONT_NORMAL -underline 0
   .mbar.options.menu add command -label "Scripts ..." -command \
      { Show_Script_Setup } -font $FONT_NORMAL -underline 4
   .mbar.options.menu add separator
   .mbar.options.menu add checkbutton -label \
      "Ping when connected" -variable \
      gb_ping_connection -font $FONT_NORMAL -underline 3
   .mbar.options.menu add checkbutton -label \
      "Auto-reconnect on disconnect" -variable \
      gb_auto_reconnect -font $FONT_NORMAL -underline 0
   .mbar.options.menu add checkbutton -label \
      "Reset clock on connect" -variable \
      gb_reset_clock_on_connect -font $FONT_NORMAL -underline 0
   .mbar.options.menu add checkbutton -label \
      "Beep on (dis)connect" -variable \
      gb_beep_on_connect -font $FONT_NORMAL -underline 0
   .mbar.options.menu add separator
   .mbar.options.menu add checkbutton -label \
      "Check for Mail" -variable \
      gb_check_for_mail -font $FONT_NORMAL -underline 10
   .mbar.options.menu add checkbutton -label \
      "Beep on new mail" -variable \
      gb_beep_on_new_mail -font $FONT_NORMAL -underline 8
   .mbar.options.menu add separator
   .mbar.options.menu add checkbutton -label \
      "Confirm quit" -variable \
      gb_confirm_quit -font $FONT_NORMAL -underline 0
   .mbar.options.menu add checkbutton -label \
      "Save window positions" -variable \
      gb_save_positions -font $FONT_NORMAL -underline 5
   .mbar.options.menu add checkbutton -label \
      "Startup Iconified" -variable \
      gb_startup_iconified -font $FONT_NORMAL -underline 8
   .mbar.options.menu add separator
   .mbar.options.menu add checkbutton -label \
      "Show time information" -variable \
      gb_show_time -font $FONT_NORMAL -underline 5 \
      -command {Show_Time $gb_show_time}
   .mbar.options.menu add checkbutton -label \
      "Show throughput" -variable \
      gb_show_stats -font $FONT_NORMAL -underline 6 \
      -command {Show_Stats $gb_show_stats}
   .mbar.options.menu add checkbutton -label \
      "Show buttons" -variable \
      gb_show_buttons -font $FONT_NORMAL -underline 5 \
      -command {Show_Buttons $gb_show_buttons}
   .mbar.options.menu add checkbutton -label \
      "Show messages" -variable \
      gb_show_messages -font $FONT_NORMAL -underline 0 \
      -command {Show_Messages $gb_show_messages}
   .mbar.options.menu add separator
   .mbar.options.menu add command -label "Reset options to defaults" \
      -command { Source_Options 0 } -font $FONT_NORMAL -underline 17
   .mbar.options.menu add command -label "Save options" -command \
      { Save_Options } -font $FONT_NORMAL -underline 5

menu .mbar.launcher.menu -tearoff 0
   .mbar.launcher.menu add command -label "Setup ..." -command \
      { Show_Launcher_Setup } -font $FONT_NORMAL -underline 0
   .mbar.launcher.menu add separator
   .mbar.launcher.menu add command -label \
      "Show toolbar" -command {Show_Launcher \
      $gb_launcher_window} -font $FONT_NORMAL -underline 6
   .mbar.launcher.menu add command -label \
      "Hide toolbar" -command {if [winfo exists .launcher_window] {
      destroy .launcher_window}} -font $FONT_NORMAL -underline 6
   .mbar.launcher.menu add separator
   .mbar.launcher.menu add checkbutton -label \
      "Show toolbar at startup" -variable \
      gb_show_launcher_at_startup -font $FONT_NORMAL \
      -underline 1
   .mbar.launcher.menu add checkbutton -label \
      "Toolbar uses separate window" -variable \
      gb_launcher_window -font $FONT_NORMAL -underline 22 \
      -command {Show_Launcher $gb_launcher_window}
   .mbar.launcher.menu add checkbutton -label \
      "Toolbar vertically oriented" -variable \
      gb_launcher_vertical -font $FONT_NORMAL -underline 8 \
      -command {Show_Launcher $gb_launcher_window}
   .mbar.launcher.menu add separator
   .mbar.launcher.menu add radiobutton -label \
      "Show Items as labels" -variable \
      gi_launcher_button_style -font $FONT_NORMAL \
      -underline 5 -value 1 -command {Show_Launcher \
      $gb_launcher_window}
   .mbar.launcher.menu add radiobutton -label \
      "Show Items as bitmaps" -variable \
      gi_launcher_button_style -font $FONT_NORMAL \
      -underline 5 -value 2 -command {Show_Launcher \
      $gb_launcher_window}
   .mbar.launcher.menu add radiobutton -label \
      "Show both labels and bitmaps" -variable \
      gi_launcher_button_style -font $FONT_NORMAL \
      -underline 5 -value 3 -command {Show_Launcher \
      $gb_launcher_window}

menu .mbar.help.menu -tearoff 0
   .mbar.help.menu add command -label "Site-specific" -command \
      {Mini_Help gs_site_specific_help} \
      -font $FONT_NORMAL -underline 0
   .mbar.help.menu add command -label "General" -command \
      {Mini_Help gs_help_text_general} \
      -font $FONT_NORMAL -underline 0
   .mbar.help.menu add command -label "On Menus" -command \
      {Mini_Help gs_help_text_menus} \
      -font $FONT_NORMAL -underline 3
   .mbar.help.menu add command -label "On Image Types" -command \
      {Mini_Help gs_help_on_images} \
      -font $FONT_NORMAL -underline 3
   .mbar.help.menu add command -label "On Launcher" -command \
      {Mini_Help g_launcher_setup_text_help} \
      -font $FONT_NORMAL -underline 3
   .mbar.help.menu add command -label "Announce" -command \
      {Mini_Help gs_help_text_release} \
      -font $FONT_NORMAL -underline 0
   .mbar.help.menu add separator
   .mbar.help.menu add checkbutton -label \
      "Show Microhelp" -variable gb_show_microhelp \
      -font $FONT_NORMAL -underline 0

tk_menuBar .mbar .mbar.file .mbar.windows .mbar.options \
   .mbar.launcher .mbar.help

###############################################################
# Create the toolbar
if {$gb_show_launcher_at_startup} {
   Show_Launcher $gb_launcher_window
}

###############################################################
# Create the status panel
frame .status_fr -borderwidth $RIDGE_BORDER -relief groove
pack .status_fr -padx $DEFAULT_PADDING -pady $DEFAULT_PADDING \
   -side top -fill x

# Connected status row
frame .status_fr.connect_fr
message .status_fr.connect_fr.status_label -text \
   "Connection Status " \
   -font $FONT_NORMAL -justify center \
   -aspect 500
set g_connection_status [button .status_fr.connect_fr.status -text \
   "Disconnected" -highlightbackground $GREEN \
   -font $FONT_BOLD -justify center \
   -background $GREEN -command Toggle_Connection]
pack .status_fr.connect_fr.status_label .status_fr.connect_fr.status \
   -side left -padx $DEFAULT_PADDING -pady $DEFAULT_PADDING

# Set the pixmap if one is specified
if {$g_disconnected_pixmap != ""} {
   set Image [Create_Image $g_disconnected_pixmap]
   if {$Image != -1} {
      $g_connection_status configure -image $Image
   }
}

# For stats row
frame .status_fr.stats_frame
message .status_fr.stats_frame.label -text "Link Throughput   " \
   -font $FONT_NORMAL -justify center -aspect 600
set g_throughput [message .status_fr.stats_frame.stats -text \
   "0.00 Pkts/s" -font $FONT_BOLD -justify center \
   -aspect 400 -borderwidth $LOWER_BORDER]
pack .status_fr.stats_frame.label .status_fr.stats_frame.stats \
   -side left -padx $DEFAULT_PADDING -pady $DEFAULT_PADDING

# For time row
frame .status_fr.time_fr
message .status_fr.time_fr.time_label -text \
   "Time Connected   " \
   -font $FONT_NORMAL -justify center \
   -aspect 600
set g_connection_time [message .status_fr.time_fr.time -text \
   "000/00:00:00" -font $FONT_BOLD -justify center \
   -aspect 400 -borderwidth $LOWER_BORDER]
pack .status_fr.time_fr.time_label .status_fr.time_fr.time -side left \
   -padx $DEFAULT_PADDING -pady $DEFAULT_PADDING

# Pack the columns
pack .status_fr.connect_fr -side top -anchor w
Show_Time $gb_show_time
Show_Stats $gb_show_stats

###############################################################
# Create the message label
message .message_text -text \
   "Ok" -font $FONT_NORMAL -justify center \
   -aspect 1500 -foreground $GREEN -relief groove
Show_Messages $gb_show_messages

###############################################################
# Create the buttons below the frame
frame .button_frame -borderwidth $DEFAULT_PADDING
button .button_frame.connect -text Connect -font $FONT_NORMAL \
   -command {Net_Connect}
button .button_frame.disconnect -font $FONT_NORMAL \
    -text Disconnect -command {Net_Disconnect}
button .button_frame.deliver -font $FONT_NORMAL \
    -text Deliver -command {Deliver_Mail}
button .button_frame.quit -font $FONT_NORMAL \
    -text Quit -command {Quit}
button .button_frame.log -font $FONT_NORMAL -text "Log ..." \
    -command {Show_Log}
button .button_frame.mail -font $FONT_NORMAL -text "Mail ..." \
    -command {Show_Mail}
pack .button_frame.quit .button_frame.disconnect \
   .button_frame.connect .button_frame.deliver -side right
pack .button_frame.log .button_frame.mail -side left
Show_Buttons $gb_show_buttons

###############################################################
# Add binding for a popup menu (button 3)
Create_Popup_Menu
bind . <Button-3> {Popup_Menu . .popup_menu}

# Add bindings for microhelp 
bind .button_frame.connect <Enter> {Set_Microhelp $gs_help_connect} 
bind .button_frame.disconnect <Enter> {Set_Microhelp $gs_help_disconnect}
bind .button_frame.quit <Enter> {Set_Microhelp $gs_help_quit}
bind .button_frame.deliver <Enter> {Set_Microhelp $gs_help_deliver} 
bind .button_frame.mail <Enter> {Set_Microhelp $gs_help_mail} 
bind .button_frame.log <Enter> {Set_Microhelp $gs_help_log}
bind $g_connection_time <Enter> {Set_Microhelp $gs_help_time}
bind $g_throughput <Enter> {Set_Microhelp $gs_help_through}
bind $g_connection_status <Enter> {Set_Microhelp $gs_help_status}
bind .message_text <Enter> {Set_Microhelp $gs_help_popup}

###############################################################
# Start background processing
if {$g_network_check_freq > 0} {
   set run_after [ expr $g_network_check_freq * 1000 ]
} else {set run_after 10000}
after $run_after Check_For_Connection
after $run_after Biff 0

###############################################################
# Set a welcome message for the user
set message [join [list $TKNET_USER \
   "- Welcome to TkNet 1.0"]]
Set_Message $message $RED

# Start throughput calculations
Update_Statistics
      
