/*  xtraceroute - graphically show traceroute information.
 *  Copyright (C) 1996-1998  Björn Augustsson 
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include "xt.h"
#include <GL/glx.h>
#include <sys/stat.h>

#include "trackball.h"

gint mouse_motion(GtkWidget *, GdkEventMotion *);
gint get_from_traceroute(FILE *, int, GdkInputCondition);
void parse_row_from_traceroute(char *);

GtkWidget *clist;

texture *earth_texture;

int xbegin, ybegin, zbegin;

GLfloat zoom = 1.0;
float curquat[4];

GLint vp[4];

int scanning = 0;  /* Are we waiting for input from traceroute? */

int newModel = 1;
FILE *ptr;

static const char* versionstring = N_("Xtraceroute version ");

static void clear_sites(void)
{
    extern const int n_countries;
    int i;
    
    for(i=0;i<MAX_SITES;i++)
      sites[i].draw = 0;
    printf(_("Known countries: %d\n"
	   "Built-in database: %d\n"), n_countries, internal->n_entries); 
}

void recalcModelView(void)
{
  GLfloat m[4][4];
 
  glPopMatrix();
  glPushMatrix();

  build_rotmatrix(m, curquat);
  glMultMatrixf(&m[0][0]);

  glScalef(zoom,zoom,zoom);
  newModel = 0;
}

void redraw(GtkWidget *wi, GdkEvent *gdk_event)
{
  if (gtk_gl_area_begingl(GTK_GL_AREA(wi)))
    {
      if (newModel)
	recalcModelView();
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glCallList(WORLD);
      
      gtk_gl_area_endgl(GTK_GL_AREA(wi));
    }
  gtk_gl_area_swapbuffers(GTK_GL_AREA(wi));
}


int which_site(GLint x, GLint y)
{
  GLuint selectBuf[MAXSELECT];
  GLint hits = 0;
  GLint i;
  
  if (gtk_gl_area_begingl(GTK_GL_AREA(glarea)))
    {
      glSelectBuffer(MAXSELECT, selectBuf);
      glRenderMode(GL_SELECT);
      glInitNames();
      glPushName(~0);
      glMatrixMode(GL_PROJECTION);
      glPushMatrix();
      glLoadIdentity();
      gluPickMatrix(x, glarea->allocation.height - y, 1, 1, vp);
      gluPerspective(40.0, 1.0, 1.0, 40.0);
      glMatrixMode(GL_MODELVIEW);
    
      gtk_gl_area_endgl(GTK_GL_AREA(glarea));
    }
  set_render_mode(SELECT_MODE);
  makeearth();
  set_render_mode(NORMAL_MODE);
  
  if (gtk_gl_area_begingl(GTK_GL_AREA(glarea)))
    {
      glCallList(WORLD);
      hits = glRenderMode(GL_RENDER);
      
      /* Change back to projection here to pop my old matrix.
	 Otherwise we'll look at the world through the pickmatrix
	 in the future.            */
      
      glMatrixMode(GL_PROJECTION);
      glPopMatrix();
      glMatrixMode(GL_MODELVIEW);

      gtk_gl_area_endgl(GTK_GL_AREA(glarea));      
    }

  if(hits <= 0)
    return -1;
  
  for(i=0 ; i<hits ; i++)
    if(selectBuf[3 + 4*i] != -1)
      return(selectBuf[3 + 4*i]);     
  return -1;
}

void reshape(GtkWidget *wi, gpointer data)
{
  gint v;
    
  if(!GTK_WIDGET_REALIZED (wi))
    {
      gtk_widget_queue_resize(wi);
      return;
    }
  if (gtk_gl_area_begingl(GTK_GL_AREA(wi)))
    {
      gint w = wi->allocation.width;
      gint h = wi->allocation.height;
      
      v = w<h?w:h;
      
      glViewport((w-v)/2, (h-v)/2, v, v);
      glGetIntegerv(GL_VIEWPORT, vp);
      gtk_gl_area_endgl(GTK_GL_AREA(wi));
      gtk_gl_area_swapbuffers(GTK_GL_AREA(wi));
    }
}

gint input_tag = 0;

/* Calls traceroute if the argument is 0, else reads debug input from stdio. */

void calltrace(int debug_input)
{
  /*   I don't know if there are several versions of traceroute out there,
       but hopefully they can be configured to deliver what I want with this.
       Otherwise, this function must be hacked some more.  */
  
  /*   New development: SGI's popen() does not behave in the same way that
       Sun's do. Sun's version executes the command in a /bin/sh, and SGI's
       seems to run it in the default shell of the running user! *DUMB*
       Oh well. Have to rehack it I guess...    */
  
  /*   Apparently HP/UX 10.20's traceroute/popen() works like suns. Good.   */
  
  char tracepgm[]   = TRACEPGM;
  char traceopts[]  = " -q 2 -w 20 ";
  char traceredir[] = " 2>/dev/null";    
  char cmdline[200] = "";
  int test;
  
  // g_print("calltrace\n");

  if(debug_input)
    ptr = stdin;
  else
    {
      strcat(cmdline,tracepgm);
      strcat(cmdline,traceopts);
      strcat(cmdline,currentloc);
      strcat(cmdline,traceredir);
      fflush(ptr);                             /*   Just in case...  */
      if((ptr = popen(cmdline,"r")) == NULL)   /*  Call traceroute.  */
	{
	  char tmp[200];
	  strcat(tmp,"xtraceroute: Couldn't open pipe to ");
	  strcat(tmp, tracepgm);
	  strcat(tmp, "!");
	  tell_user(tmp);
	  spinner_stop();
	  return;
	}
    }
  /* See if the stream contains any info */
  test = getc(ptr);
  if(test == EOF)
    {
      char tmp[200];
      spinner_stop();
      pclose(ptr);
      ptr = NULL;
      strcpy(tmp, _("xtraceroute: unknown host "));
      strcat(tmp, currentloc);
      tell_user(tmp);
      scanning = 0;
      return;
    }
  /* The stream is OK, push the char back */
  
  ungetc(test, ptr);
  // g_print("stream OK.\n");
  
  scanning = 1;
  spinner_start();
  
  input_tag = gdk_input_add(fileno(ptr), GDK_INPUT_READ, 
			    (GdkInputFunction)get_from_traceroute, ptr);
}


// have this un-globalized or put in a separate file later
gint track_tag = 0;

/**
 * Called when the mouse buttons gets pressed on the glarea.
 */

gint mouse_button_down(GtkWidget *wi, GdkEventButton *ev)
{
  int site;
  int x = (int)ev->x;
  int y = (int)ev->y;
  
  switch(ev->button)
    {
    case 1:
      
      /* In case the user is trying to select a site */
      site = which_site(x, y);
      if(site != -1)
	{
	  gtk_clist_select_row(GTK_CLIST(clist), site, 1);
	  
	  /* Scroll the list here to make sure the listitem
	     is visible in the window. */
	  
	  gtk_clist_moveto(GTK_CLIST(clist),site, 1, 0.5, 0.5);
	  return TRUE;
	}
      /* He wasn't, he's trying to rotate the earth. */
      
      xbegin = x;
      ybegin = y;
      if(track_tag == 0)  /* To prevent duplicate callbacks. */
	track_tag = gtk_idle_add((GtkFunction)mouse_motion, wi);
      
      break;
    }
  return TRUE;
}

gint mouse_button_up(GtkWidget *wi, GdkEventButton *ev)
{
  if(ev->button == 1 && track_tag != 0) 
    { 
      gtk_idle_remove(track_tag);
      track_tag = 0;
    }
  return TRUE;
}

gint mouse_motion(GtkWidget *wi, GdkEventMotion *ev)
{
  /* The sensitivity is a bit low FIXME*/
  float tempquat[4];
  gint x, y, w, h;
  GdkModifierType mods;
  gdk_window_get_pointer (wi->window, &x, &y, &mods);
  if(x == xbegin && y == ybegin)
    return TRUE;

  w = glarea->allocation.width;
  h = glarea->allocation.height;

  /* This stuff doesn't really work well unless the 
     canvas is quadratic. */

  trackball(tempquat,
	    (2.0*xbegin - w) / w,
	    (h - 2.0*ybegin) / h,
	    (2.0*x - w) / w,
	    (h - 2.0*y) / h);

  add_quats(tempquat, curquat, curquat);

  xbegin = x;
  ybegin = y;

  newModel = 1;
  redraw(glarea, NULL);
  
  return TRUE;
}

char row_so_far[300];
int  buffer_counter = 0;

gint get_from_traceroute(FILE *stream, int watchpoint, GdkInputCondition cond)
{
  int c;
  
  if(scanning == 0)
    {
      /* FIX for API brokenness. This callback should unregister if it 
	 returns false, as all other callbacks (in gtk at least) do. */
      gdk_input_remove(input_tag);      

      return FALSE;
    }
  if(cond == GDK_INPUT_READ)
    {
      c = getc(stream);
      if(c == EOF)
	{
	  pclose(stream);
	  stream = NULL;
	  ptr = NULL;
	  scanning = 0;
	  makeearth();
	  spinner_stop();
	  gdk_input_remove(input_tag); /* Fix for bad API. See above. */
	  return FALSE;
	}
      if(c == '*')
	return TRUE;
      row_so_far[buffer_counter++] = c;
      
      if(c == '\n')
	{
	  row_so_far[buffer_counter] = '\0';
	  parse_row_from_traceroute(row_so_far);
	  buffer_counter = 0;
	}
    }
  return TRUE;
} 

void parse_row_from_traceroute(char *input)
{
  int i;
  int no;
  int last;
  char *clistrow[3];
  
  /* This is a SGI compatibility thing; On SGI "traceroute" prints
     the first line "traceroute to test.com (0.0.0.0), 30 hops max, 
     40 byte packets" to stdout instad of stderr (which most other
     machines does). FWIW, I actually think SGI's behavior is saner 
     (stderr is for errors!), but... */
  /* On the other hand, SGI-traceroute prints errors as well to 
     stdout... Oh well... */
  
  if(!strncasecmp("traceroute",input, 10))
    {
      //      printf("SGI weird traceroute!\n");
      return;
    }
  else if(isin("unknown host", input))
    {
      /*  This code is common with some in calltrace. FIXME. */
      char tmp[200];
      spinner_stop();
      gdk_input_remove(input_tag);
      pclose(ptr);
      ptr = NULL;
      strcpy(tmp, _("xtraceroute: unknown host "));
      strcat(tmp, currentloc);
      tell_user(tmp);
      scanning = 0;
      return;
    }
  else if(strlen(input)< 10) // All we got was a couple of spaces, from a "XX * * *"-row.
    {
      sscanf(input,"%d",&no);
      no--;
      strcpy(sites[no].name, "No response");
      strcpy(sites[no].ip  , "No response");
      strcpy(sites[no].info, "There were no response from this machine.");
      sites[no].lat = sites[no-1].lat; // FIXME:
      sites[no].lon = sites[no-1].lon; //potential trouble here if no == 0.
      sites[no].time = 0;
      sites[no].accuracy = ACC_NONE;
      sites[no].draw = 1;
    }
  else /* All is well, do the normal stuff. */
    {
      i = sscanf(input,"%d",&no);
      
      /* Skip past the first number. */
      while(!is_whitespace(input[i]))
	i++;
      no--;
      sscanf(input+i,"%s %*c%s %d",sites[no].name,sites[no].ip,&sites[no].time);
      
      /* Get rid of the trailing ')' from the IP... */
      sites[no].ip[ strlen(sites[no].ip) - 1 ] = '\0';

      resolve_by_id(no);
    }
  
  clistrow[0] = (char *)malloc(10);
  clistrow[1] = (char *)malloc(sizeof(sites[no].name));
  clistrow[2] = (char *)malloc(sizeof(sites[no].ip));
  
  sprintf(clistrow[0],"%d",no);
  sprintf(clistrow[1],"%s",sites[no].name);
  sprintf(clistrow[2],"%s",sites[no].ip);
  
  /* Add an entry to the list */
  last = gtk_clist_append(GTK_CLIST(clist), clistrow);
  
  /* Free the clistrow here */
  free(clistrow[0]);
  free(clistrow[1]);
  free(clistrow[2]);
  /* The above SHOULD be done! But there's an error here somewhere.
     Doing that causes malloc in glibc-linux to dump sometimes. 
     Investigating... This just has to be a glibc bug... Efence finds
     nothing... (This comment kept in case it shows up again.) */
  
  /* Scroll the clist so the new entry is visible */
  gtk_clist_moveto(GTK_CLIST(clist), last, 1, 1.0, 0.5);
  
  if(no >= MAX_SITES-1)
    {
      pclose(ptr);
      ptr = NULL;
      scanning = 0;
      gdk_input_remove(input_tag);
      spinner_stop();
      makeearth();
    }
  return;
}

static void exit_program(GtkWidget *wi, gpointer *data)
{
  if(ptr)
    pclose(ptr);
  gtk_main_quit();
}

gint about_program(GtkWidget *wi, gpointer *data)
{
  char mess[501];
  snprintf(mess,500,_("%s%s\nBy Björn Augustsson (d3august@dtek.chalmers.se)\n"
	  "Official homepage: http://www.dtek.chalmers.se/~d3august/xt\n")
	  ,_(versionstring),VERSION);
  tell_user(mess);
  return TRUE;
}

gint clist_item_selected(GtkCList *parentlist, gint row, gint column,
			 GdkEventButton *event)
{
  int i;
  
  if (event && event->type == GDK_2BUTTON_PRESS)
    info_window(row); /* Double click detected */
  else
    {
      /* Clear all other sites (so two sites can't be selected */
      for(i=0;i<MAX_SITES;i++)
	sites[i].selected = 0;
      
      sites[row].selected = 1;
      
      infowin_change_site(row);
      
      makeearth();
    }
  return TRUE;
}

gint info_button_callback(GtkWidget *wi, gpointer *data)
{
  int i;
  
  for(i=0;i<MAX_SITES;i++)
    if(sites[i].selected)
      {
	info_window(i);
	return TRUE;
      }
  /* Get into info-about-next-item-mode */
  return TRUE;
}

static void usage(void)
{
  g_print(_("%s\n\n"
	  "Usage: xtraceroute [--texture <texturename>]\n"
	  "                   [--lod <level-of-detail>]\n"
	  "                   [--stdio | <site> ]\n"
	  "                   [--version]\n")
	  ,_(versionstring), VERSION);
}

int main(int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *vbox2;
  GtkWidget *pane;
  GtkWidget *spinner;
  GtkWidget *info_button;
  GtkWidget *menubar;
  GtkWidget *filerootmenu;
  GtkWidget *filemenu;
  GtkWidget *helprootmenu;
  GtkWidget *helpmenu;
  GtkWidget *dbrootmenu;
  GtkWidget *dbmenu;
  GtkWidget *file_newmenuitem;
  GtkWidget *file_sepmenuitem;
  GtkWidget *file_quitmenuitem;
  GtkWidget *help_aboutmenuitem;
  GtkWidget *db_addhostmenuitem;
  GtkWidget *db_addgenmenuitem;
  GtkWidget *db_addnetmenuitem;
  GtkWidget *notebook;
  GtkWidget *label;
#ifdef GTK_HAVE_FEATURES_1_1_5
  GtkWidget *dummyscrwin;
#endif
  
  int attribs[] = {GDK_GL_RGBA,
		   GDK_GL_RED_SIZE,1,
		   GDK_GL_GREEN_SIZE,1,
		   GDK_GL_BLUE_SIZE,1,
		   GDK_GL_DEPTH_SIZE,1,
		   GDK_GL_DOUBLEBUFFER,
		   GDK_GL_NONE};

  int i;
  int has_site = FALSE;
  int has_texture = FALSE;
  int input_from_stdio = FALSE;
  struct stat statbuf;
  char *titles[] =
  {
    N_("Nr"),
    N_("Hostname"),
    N_("IP number")
  };
  static int translated;

  setlocale (LC_ALL, "");
  bindtextdomain(PACKAGE, LOCALEDIR);
  textdomain(PACKAGE);

  gtk_init(&argc, &argv);

  if (gdk_gl_query() == FALSE) 
    {
      g_print(_("OpenGL not supported\n"));
      return 0;
    }

  trackball(curquat, 0.0, 0.0, 0.0, 0.0);

  strcpy(currentloc,"");
  
  for(i=1 ; i<argc ; i++)
    {
        if(!strcmp(argv[i],"--version"))
	  {
	    g_print("%s%s\n", _(versionstring), VERSION);
	    exit(0);
	  }
	else if(!strcmp(argv[i],"--help")
		|| !strcmp(argv[i],"-h"))
	  {
	    usage();
	    exit(0);
	  }
	else if(!strcmp(argv[i],"--texture") 
		|| !strcmp(argv[i],"-T"))
	  {
	    if(has_texture == TRUE)
	      g_print(_("Two textures specified! Using second one.\n"));
	    earth_texture    = readTexture(argv[++i]);
	    has_texture = TRUE;
	  }
	else if(!strcasecmp(argv[i],"--stdio"))
	  {
	    has_site = TRUE;
	    input_from_stdio = TRUE;
	  }
	else if(!strcasecmp(argv[i],"--LOD"))
	  {
	    set_sphere_lod(atoi(argv[++i]));
	  }
	else // Has to be a site to traceroute.
	  {
	    if(has_site == TRUE)
	      {
		usage();
		exit(0);
	      }
	    strcpy(currentloc, argv[i]);
	    has_site = TRUE;
	  }
    }
  
  if(stat(TRACEPGM, &statbuf) < 0)
    {
      char tmp[501];
      snprintf(tmp,500,_("Can't find the traceroute binary!\n"
	     "I'm looking for %s"),TRACEPGM);
      perror(tmp);
      exit(0);
    }
  
  if(has_site == FALSE && !input_from_stdio)
    new_trace(NULL, NULL);
  else
    calltrace(input_from_stdio);

  if(has_texture == FALSE)
    {
      char texname[200];
      strcpy(texname, DATADIR);
      strcat(texname, "/earth.tif");
      earth_texture = readTexture(texname);
    }
  /*   Edouard
  DBs[USER][ HOSTS ] = readHostDB("user_hosts.cache");
  DBs[USER][ NETS  ] = readNetDB ("user_networks.cache");
  DBs[USER][GENERIC] = readGenDB ("user_generic.cache");

  DBs[SITE][ HOSTS ] = readHostDB("site_hosts.cache");
  DBs[SITE][ NETS  ] = readNetDB ("site_networks.cache");
  DBs[SITE][GENERIC] = readGenDB ("site_generic.cache");

  DBs[GLOBAL][ HOSTS ] = readHostDB("hosts.cache");
  DBs[GLOBAL][ NETS  ] = readNetDB ("networks.cache");
  DBs[GLOBAL][GENERIC] = readGenDB ("generic.cache");
  */

  ndg_hosts          = readHostDB("hosts.cache");
  local_site_hosts   = readHostDB("site_hosts.cache");
  local_user_hosts   = readHostDB("user_hosts.cache");
  
  //  writeHostDB(ndg_hosts, "/usr/scratch/host_apa");
  
  ndg_nets           = readNetDB("networks.cache");
  local_site_nets    = readNetDB("site_networks.cache");
  local_user_nets    = readNetDB("user_networks.cache");

  //  local_site_generic = readGenDB("generic.cache");
  //  local_site_generic = readGenDB("site_generic.cache");
  local_user_generic = readGenDB("user_generic.cache");

  
  internal = init_internal_db();
  clear_sites();

  //  writeNetDB(ndg_nets, "/usr/scratch/net_apa");
  
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_widget_set_usize (GTK_WIDGET(window), W, H);
  
  /* FIXME  Fix this to work with pseudocolor visuals. */
  glarea = gtk_gl_area_new(attribs);

  vbox    = gtk_vbox_new(FALSE,0);
  pane    = gtk_vpaned_new();

  if (!translated){
        int i;
        for (i = 0; i < 3; i++)
            titles [i] = _(titles [i]);
        translated = 1;
  }

  clist   = gtk_clist_new_with_titles(3, titles);
  
  gtk_clist_set_selection_mode((GtkCList *)clist, GTK_SELECTION_BROWSE);
  
  /* FIXME: These should probably be font-dependent. */
  
  gtk_clist_set_column_width (GTK_CLIST(clist), 0, 20);
  gtk_clist_set_column_width (GTK_CLIST(clist), 1, 230);
  gtk_clist_set_column_width (GTK_CLIST(clist), 2, 100);
  
  gtk_signal_connect(GTK_OBJECT(clist), "select_row",
		     GTK_SIGNAL_FUNC(clist_item_selected), NULL);
 
  gtk_signal_connect (GTK_OBJECT (window), "destroy",
		      GTK_SIGNAL_FUNC(exit_program), NULL);
  {
  char tmp[501];
  snprintf(tmp,500,"%s%s",_(versionstring),VERSION);
  gtk_window_set_title (GTK_WINDOW (window), tmp);
  }
  
  gtk_widget_set_usize(GTK_WIDGET(glarea), W, W);
  
#if 0
  /* Set the minimum size:  */
  gtk_drawing_area_size(GTK_DRAWING_AREA (glarea), 75, 75);
  /* Set the default size:  */
  {
    GtkAllocation allocation = { 0, 0, W, W };
    gtk_widget_size_allocate (glarea, &allocation);
  }
  g_print("apa\n");
#endif
  gtk_container_add(GTK_CONTAINER(window), vbox);
  
  gtk_widget_set_events(glarea, GDK_EXPOSURE_MASK
			| GDK_BUTTON_PRESS_MASK
			| GDK_BUTTON_RELEASE_MASK);
  
  gtk_signal_connect(GTK_OBJECT(glarea), "expose_event",
		     (GtkSignalFunc)redraw,
		     (gpointer)NULL);
  gtk_signal_connect(GTK_OBJECT(glarea), "button_press_event",
		     (GtkSignalFunc)mouse_button_down,
		     (gpointer)NULL);
  gtk_signal_connect(GTK_OBJECT(glarea), "button_release_event",
		     (GtkSignalFunc)mouse_button_up,
		     (gpointer)NULL);
  gtk_signal_connect(GTK_OBJECT(glarea), "motion_notify_event",
		     (GtkSignalFunc)mouse_motion,
		     (gpointer)NULL);
  gtk_signal_connect(GTK_OBJECT(glarea), "size_allocate",
		     (GtkSignalFunc)reshape,
		     (gpointer)NULL);
  gtk_signal_connect(GTK_OBJECT(glarea), "realize",
		     (GtkSignalFunc)init_gl,
		     (gpointer)NULL);
  
  menubar      = gtk_menu_bar_new();
  filerootmenu = gtk_menu_new();
  filemenu     = gtk_menu_item_new_with_label(_("File"));
  helprootmenu = gtk_menu_new();
  helpmenu     = gtk_menu_item_new_with_label(_("Help"));
  dbrootmenu   = gtk_menu_new();
  dbmenu       = gtk_menu_item_new_with_label(_("Database"));
    
  gtk_menu_item_right_justify (GTK_MENU_ITEM(helpmenu));
  
  gtk_widget_show(filemenu);
  gtk_widget_show(helpmenu);
  gtk_widget_show(dbmenu);
  
  file_newmenuitem = gtk_menu_item_new_with_label(_("New..."));
  gtk_signal_connect(GTK_OBJECT(file_newmenuitem), "activate",
		     (GtkSignalFunc)new_trace,
		     (gpointer)NULL);
  gtk_menu_append(GTK_MENU(filerootmenu), file_newmenuitem);
  
  /* Add a separator */
  file_sepmenuitem = gtk_menu_item_new();
  gtk_menu_append(GTK_MENU(filerootmenu), file_sepmenuitem);
  
  file_quitmenuitem     = gtk_menu_item_new_with_label(_("Quit"));
  gtk_signal_connect(GTK_OBJECT(file_quitmenuitem), "activate",
		     (GtkSignalFunc)exit_program,
		     (gpointer)NULL);
  gtk_menu_append(GTK_MENU(filerootmenu), file_quitmenuitem);
  
  help_aboutmenuitem   = gtk_menu_item_new_with_label(_("About..."));
  gtk_signal_connect(GTK_OBJECT(help_aboutmenuitem), "activate",
		     (GtkSignalFunc)about_program, (gpointer)NULL);
  gtk_menu_append(GTK_MENU(helprootmenu), help_aboutmenuitem);
  
  db_addhostmenuitem   = gtk_menu_item_new_with_label(_("Add host..."));
  gtk_signal_connect(GTK_OBJECT(db_addhostmenuitem), "activate",
		     (GtkSignalFunc)addHost,
		     (gpointer)NULL);
  gtk_menu_append(GTK_MENU(dbrootmenu), db_addhostmenuitem);
  
  db_addnetmenuitem   = gtk_menu_item_new_with_label(_("Add net..."));
  gtk_signal_connect(GTK_OBJECT(db_addnetmenuitem), "activate",
		     (GtkSignalFunc)addNet,
		     (gpointer)NULL);
  gtk_menu_append(GTK_MENU(dbrootmenu), db_addnetmenuitem);

  db_addgenmenuitem   = gtk_menu_item_new_with_label(_("Add keyword..."));
  gtk_signal_connect(GTK_OBJECT(db_addgenmenuitem), "activate",
		     (GtkSignalFunc)addGen,
		     (gpointer)NULL);
  gtk_menu_append(GTK_MENU(dbrootmenu), db_addgenmenuitem);
  
  gtk_menu_item_set_submenu(GTK_MENU_ITEM (filemenu), filerootmenu);
  gtk_menu_item_set_submenu(GTK_MENU_ITEM (helpmenu), helprootmenu);
  gtk_menu_item_set_submenu(GTK_MENU_ITEM (dbmenu),   dbrootmenu);
  
  gtk_menu_bar_append(GTK_MENU_BAR (menubar), filemenu);
  gtk_menu_bar_append(GTK_MENU_BAR (menubar), dbmenu);
  gtk_menu_bar_append(GTK_MENU_BAR (menubar), helpmenu);
  
  gtk_widget_show(file_newmenuitem);
  gtk_widget_show(file_sepmenuitem);
  gtk_widget_show(file_quitmenuitem);
  gtk_widget_show(help_aboutmenuitem);
  gtk_widget_show(db_addhostmenuitem);
  gtk_widget_show(db_addnetmenuitem);
  gtk_widget_show(db_addgenmenuitem);
  gtk_widget_show(menubar);
  
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), pane, TRUE, TRUE, 0);
  
  spinner = spinner_new();
  info_button = gtk_button_new_with_label(_("Info"));
  gtk_signal_connect(GTK_OBJECT(info_button), "clicked",
		     (GtkSignalFunc)info_button_callback,
		     (gpointer)NULL);
  
  hbox  = gtk_hbox_new(FALSE, 0);
  vbox2 = gtk_vbox_new(FALSE, 5);

  gtk_container_border_width(GTK_CONTAINER(vbox2), 5);

#ifdef GTK_HAVE_FEATURES_1_1_5

  dummyscrwin = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dummyscrwin),
				 GTK_POLICY_AUTOMATIC,
				 GTK_POLICY_AUTOMATIC);
  gtk_widget_show(dummyscrwin);
  gtk_container_add(GTK_CONTAINER(dummyscrwin), clist);
  gtk_box_pack_start(GTK_BOX(hbox), dummyscrwin, TRUE, TRUE, 0);
#else   // GTK+ 1.0.x
  gtk_clist_set_policy (GTK_CLIST(clist),
			GTK_POLICY_AUTOMATIC,
			GTK_POLICY_AUTOMATIC);
  gtk_box_pack_start(GTK_BOX(hbox), clist, TRUE, TRUE, 0);
#endif		    
  
  gtk_box_pack_start(GTK_BOX(hbox), vbox2,   FALSE, TRUE, 0);
  
  gtk_box_pack_start(GTK_BOX(vbox2), spinner, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(vbox2), info_button, TRUE, TRUE, 0);
  
  notebook = gtk_notebook_new ();
  gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
  gtk_widget_show(notebook);
  
  /*  FIXME! */
  /* Now let's add all the pages. All of these are in separate files,
     and have local data. The traceroute page must be moved out of here. */
  
  /* Traceroute part: */
  label = gtk_label_new (_("Traceroute"));
  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), hbox, label);

  // for all other pages: 
  //
  // widg = create_page(...)
  // label = gtk_label_new ("page name");  
  // gtk_notebook_append_page (GTK_NOTEBOOK (notebook), widg, label);
  
  
  gtk_paned_add1(GTK_PANED(pane), glarea);
  gtk_paned_add2(GTK_PANED(pane), notebook);
  
  gtk_widget_show(clist);
  gtk_widget_show(glarea);
  gtk_widget_show(pane);
  gtk_widget_show(info_button);
  gtk_widget_show(spinner);
  gtk_widget_show(vbox2);
  gtk_widget_show(vbox);
  gtk_widget_show(hbox);
  gtk_widget_show(window);
  
  makeearth();
  
  gtk_main();
  
  return 0;             /* ANSI C requires main to return int. */
}
