/*

Copyright (C) 2000 - 2004 Christian Kreibich <christian@whoop.org>.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies of the Software and its documentation and acknowledgment shall be
given in the documentation and software packages that this Software was
used.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <sys/types.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>

#include <nd_trace_area_gui.h>
#include <nd_trace_registry.h>
#include <interface.h>
#include <nd_gui.h>
#include <callbacks.h>


static GtkWidget              *trace_area_dialog = NULL;
static LND_TPM_NavMode         pit_mode   = LND_TPM_TIME;

/* Callback that adjusts timestamp text areas and scrollbar
 * locations when the user scrolls the start- or end timestamp
 * handles.
 */
static void
pit_time_scale_cb(GtkAdjustment *adj, gboolean start)
{
  GtkWidget          *w, *start_scale, *end_scale;
  GtkAdjustment      *adj_scale;
  LND_Trace          *trace;
  char                buf[MAXPATHLEN];
  struct bpf_timeval  delta, sum;

  return_if_no_current_trace(trace);

  ND_GTK_GET(start_scale, trace_area_dialog, "nav_time_start_hscale");
  ND_GTK_GET(end_scale,   trace_area_dialog, "nav_time_end_hscale");

  pcapnav_timeval_sub(&trace->tpm->base->end_ts,
		      &trace->tpm->base->start_ts,
		      &delta);  

  delta.tv_sec  = delta.tv_sec * adj->value;
  delta.tv_usec = delta.tv_usec * adj->value;
  pcapnav_timeval_add(&delta, &trace->tpm->base->start_ts, &sum);

  ND_GTK_GET(w, trace_area_dialog, start ? "nav_time_start_sec_entry" : "nav_time_end_sec_entry");
  g_snprintf(buf, MAXPATHLEN, "%lu", (unsigned long) sum.tv_sec);
  gtk_signal_handler_block_by_func(GTK_OBJECT(w), on_pit_time_entries_changed,
				   GINT_TO_POINTER(start ? TRUE : FALSE));
  gtk_entry_set_text(GTK_ENTRY(w), buf);
  gtk_signal_handler_unblock_by_func(GTK_OBJECT(w), on_pit_time_entries_changed,
				     GINT_TO_POINTER(start ? TRUE : FALSE));

  ND_GTK_GET(w, trace_area_dialog, start ? "nav_time_start_usec_entry" : "nav_time_end_usec_entry");
  g_snprintf(buf, MAXPATHLEN, "%lu", (unsigned long) sum.tv_usec);
  gtk_signal_handler_block_by_func(GTK_OBJECT(w), on_pit_time_entries_changed,
				   GINT_TO_POINTER(start ? TRUE : FALSE));
  gtk_entry_set_text(GTK_ENTRY(w), buf);  
  gtk_signal_handler_unblock_by_func(GTK_OBJECT(w), on_pit_time_entries_changed,
				     GINT_TO_POINTER(start ? TRUE : FALSE));
  
  if (start)
    {
      adj_scale = gtk_range_get_adjustment(GTK_RANGE(end_scale));
      if (adj_scale->value < adj->value)
	{
	  adj_scale->value = adj->value;
	  gtk_adjustment_value_changed(adj_scale);
	}            
    }
  else
    {
      adj_scale = gtk_range_get_adjustment(GTK_RANGE(start_scale));
      if (adj_scale->value > adj->value)
	{
	  adj_scale->value = adj->value;
	  gtk_adjustment_value_changed(adj_scale);
	}            
    }
  
  ND_GTK_GET(w, trace_area_dialog, start ? "nav_time_start_label" : "nav_time_end_label");
  libnd_misc_ctime(&sum, buf, MAXPATHLEN, TRUE, TRUE);
  gtk_label_set_text(GTK_LABEL(w), buf);
}


static void
pit_space_scale_cb(GtkAdjustment *adj, gboolean start)
{
  GtkWidget      *start_spin, *end_spin;
  GtkWidget      *start_scale, *end_scale;
  GtkAdjustment  *adj_scale;

  ND_GTK_GET(start_spin,  trace_area_dialog, "nav_frac_start_spinbutton");
  ND_GTK_GET(end_spin,    trace_area_dialog, "nav_frac_end_spinbutton");
  ND_GTK_GET(start_scale, trace_area_dialog, "nav_frac_start_hscale");
  ND_GTK_GET(end_scale,   trace_area_dialog, "nav_frac_end_hscale");
  
  if (start)
    {
      gtk_spin_button_set_value(GTK_SPIN_BUTTON(start_spin), adj->value);
      
      adj_scale = gtk_range_get_adjustment(GTK_RANGE(end_scale));
      if (adj_scale->value < adj->value)
	{
	  adj_scale->value = adj->value;
	  gtk_adjustment_value_changed(adj_scale);
	}      
    }
  else
    {
      gtk_spin_button_set_value(GTK_SPIN_BUTTON(end_spin), adj->value);

      adj_scale = gtk_range_get_adjustment(GTK_RANGE(start_scale));
      if (adj_scale->value > adj->value)
	{
	  adj_scale->value = adj->value;
	  gtk_adjustment_value_changed(adj_scale);
	}      
    }
}


static void
pit_space_spin_cb(GtkAdjustment *adj, gboolean start)
{
  GtkWidget      *w;
  GtkAdjustment  *adj_scale;

  if (start)
    ND_GTK_GET(w, trace_area_dialog, "nav_frac_start_hscale");
  else
    ND_GTK_GET(w, trace_area_dialog, "nav_frac_end_hscale");

  adj_scale = gtk_range_get_adjustment(GTK_RANGE(w));
  adj_scale->value = adj->value;
  gtk_adjustment_value_changed(adj_scale);
}


/* Attempts to parse a timeval struct from either the start
 * or end time entry area.
 */
static gboolean
trace_area_dialog_get_timeval(struct bpf_timeval *tv, gboolean start)
{
  GtkWidget *w;
  char      *errptr;

  if (!tv || !trace_area_dialog)
    return FALSE;

  if (start)
    ND_GTK_GET(w, trace_area_dialog, "nav_time_start_sec_entry");
  else
    ND_GTK_GET(w, trace_area_dialog, "nav_time_end_sec_entry");

  tv->tv_sec = strtoul(gtk_entry_get_text(GTK_ENTRY(w)), &errptr, 10);
  if ((*errptr != '\0') || (errno == ERANGE))
    return FALSE;

  if (start)
    ND_GTK_GET(w, trace_area_dialog, "nav_time_start_usec_entry");
  else
    ND_GTK_GET(w, trace_area_dialog, "nav_time_end_usec_entry");
  
  tv->tv_usec = strtoul(gtk_entry_get_text(GTK_ENTRY(w)), &errptr, 10);
  if ((*errptr != '\0') || (errno == ERANGE))
    return FALSE;

  while (tv->tv_usec > 1000000)
    {
      tv->tv_sec++;
      tv->tv_usec -= 1000000;
    }
  
  return TRUE;
}


static LND_TraceArea *
trace_area_dialog_get_current_area(void)
{
  GtkWidget *w;
  GList *selection;
  LND_TraceArea *area;

  if (!trace_area_dialog)
    return NULL;

  ND_GTK_GET(w, trace_area_dialog, "area_list");
  if (!w)
    {
      D(("Caution -- GUI error.\n"));
      return NULL;
    }
  
  if (! (selection = GTK_LIST(w)->selection))
    return NULL;
  
  ND_GTK_GET(area, selection->data, "trace_area");
  
  return area;
}


void      
nd_trace_area_dialog_show(void)
{
  LND_Trace      *trace;
  GtkWidget      *w;
  GtkAdjustment  *adj;

  return_if_no_current_trace(trace);

  if (!trace_area_dialog)
    {
      trace_area_dialog = create_iterator_area_dialog();

      ND_GTK_GET(w, trace_area_dialog, "nav_time_start_hscale");
      adj = gtk_range_get_adjustment(GTK_RANGE(w));
      gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
			  GTK_SIGNAL_FUNC (pit_time_scale_cb), GINT_TO_POINTER(TRUE));

      ND_GTK_GET(w, trace_area_dialog, "nav_time_end_hscale");
      adj = gtk_range_get_adjustment(GTK_RANGE(w));
      gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
			  GTK_SIGNAL_FUNC (pit_time_scale_cb), GINT_TO_POINTER(FALSE));

      ND_GTK_GET(w, trace_area_dialog, "nav_frac_start_hscale");
      adj = gtk_range_get_adjustment(GTK_RANGE(w));
      gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
			  GTK_SIGNAL_FUNC (pit_space_scale_cb), GINT_TO_POINTER(TRUE));

      ND_GTK_GET(w, trace_area_dialog, "nav_frac_end_hscale");
      adj = gtk_range_get_adjustment(GTK_RANGE(w));
      gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
			  GTK_SIGNAL_FUNC (pit_space_scale_cb), GINT_TO_POINTER(FALSE));

      ND_GTK_GET(w, trace_area_dialog, "nav_frac_start_spinbutton");
      adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(w));
      gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
			  GTK_SIGNAL_FUNC (pit_space_spin_cb), GINT_TO_POINTER(TRUE));

      ND_GTK_GET(w, trace_area_dialog, "nav_frac_end_spinbutton");
      adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(w));
      gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
			  GTK_SIGNAL_FUNC (pit_space_spin_cb), GINT_TO_POINTER(FALSE));
    }

  ND_GTK_GET(w, trace_area_dialog, "area_list");
  if (GTK_LIST(w)->children == 0)
    {
      /* Disable the editing frame and some buttons until the user
       * selects/creates a trace area.
       */
      ND_GTK_GET(w, trace_area_dialog, "config_frame");
      gtk_widget_set_sensitive(w, FALSE);
      ND_GTK_GET(w, trace_area_dialog, "trace_area_del_button");
      gtk_widget_set_sensitive(w, FALSE);
      ND_GTK_GET(w, trace_area_dialog, "trace_area_set_button");
      gtk_widget_set_sensitive(w, FALSE);
    }

  nd_trace_area_dialog_sync(NULL);

  gtk_widget_show(trace_area_dialog);
}


void      
nd_trace_area_dialog_hide(void)
{
  if (trace_area_dialog)
    gtk_widget_hide(trace_area_dialog);
}


gboolean
nd_trace_area_dialog_apply(void)
{
  GtkWidget         *w;
  LND_TPM_NavMode    mode;
  struct bpf_timeval time_start, time_end;
  double             space_start, space_end;
  LND_TraceArea     *area;

  D_ENTER;

  if (!trace_area_dialog)
    D_RETURN_(FALSE);

  mode = nd_trace_area_dialog_get_mode();
  area = trace_area_dialog_get_current_area();

  switch(mode)
    {
    case LND_TPM_TIME:
      
      if (!trace_area_dialog_get_timeval(&time_start, TRUE) ||
	  !trace_area_dialog_get_timeval(&time_end, FALSE))
	{
	  nd_dialog_message(_("Timestamp Problem"),
			    _("There is an error in one of your timestamps."),
			    TRUE);
	  D_RETURN_(FALSE);
	}
      
      libnd_trace_area_init_time(area, &time_start, &time_end);
      break;

    case LND_TPM_SPACE:
    default:

      ND_GTK_GET(w, trace_area_dialog, "nav_frac_start_spinbutton");
      space_start = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(w));
      
      ND_GTK_GET(w, trace_area_dialog, "nav_frac_end_spinbutton");
      space_end = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(w));

      D(("Setting area to %f %f\n", space_start, space_end));
      libnd_trace_area_init_space(area, space_start, space_end);
      break;
      
    }

  D_RETURN_(TRUE);
}


void      
nd_trace_area_dialog_set_mode(LND_TPM_NavMode mode)
{
  GtkWidget *w;

  D_ENTER;

  D(("Setting mode to %s\n", (mode == LND_TPM_SPACE ? "space" : "time")));
  pit_mode = mode;
  ND_GTK_GET(w, trace_area_dialog, "nav_notebook");

  switch(mode)
    {
    case LND_TPM_SPACE:
      gtk_notebook_set_page(GTK_NOTEBOOK(w), 1);
      break;

    case LND_TPM_TIME:
      gtk_notebook_set_page(GTK_NOTEBOOK(w), 0);
      break;

    default:
      D_RETURN;
    }

  D_RETURN;
}


/* Callback that updates the scrollbar handles according
 * to timestamps typed into the timestamp text areas.
 */
void 
nd_trace_area_dialog_sync_time_scale(gboolean start)
{
  LND_Trace          *trace;
  GtkWidget          *w;
  GtkAdjustment      *adj, *adj_other;
  struct bpf_timeval  tv, delta, delta2;
  gfloat              f;

  if (!trace_area_dialog)
    return;

  return_if_no_current_trace(trace);
  
  /* Set name of the trace are in the GUI */

  ND_GTK_GET(w, trace_area_dialog, start ? "nav_time_start_sec_entry" : "nav_time_end_sec_entry");
  tv.tv_sec = strtol(gtk_entry_get_text(GTK_ENTRY(w)), (char **)NULL, 10);
  if (errno == ERANGE)
    return;

  ND_GTK_GET(w, trace_area_dialog, start ? "nav_time_start_usec_entry" : "nav_time_end_usec_entry");
  tv.tv_usec = strtol(gtk_entry_get_text(GTK_ENTRY(w)), (char **)NULL, 10);
  if (errno == ERANGE)
    return;
  
  pcapnav_timeval_sub(&tv, &trace->tpm->base->start_ts, &delta);
  if (delta.tv_sec == 0 && delta.tv_usec == 0)
    return;

  pcapnav_timeval_sub(&trace->tpm->base->end_ts, &trace->tpm->base->start_ts, &delta2);
  
  ND_GTK_GET(w, trace_area_dialog, start ? "nav_time_start_hscale" : "nav_time_end_hscale");
  adj = gtk_range_get_adjustment(GTK_RANGE(w));

  ND_GTK_GET(w, trace_area_dialog, start ? "nav_time_end_hscale" : "nav_time_start_hscale");
  adj_other = gtk_range_get_adjustment(GTK_RANGE(w));

  f =
    ((float) delta.tv_sec + (float) delta.tv_usec / 1000000) /
    ((float) delta2.tv_sec + (float) delta2.tv_usec / 1000000);
  
  if (f <= 0.0001 || f >= 0.99999)
    return;

  gtk_signal_handler_block_by_func(GTK_OBJECT(adj), pit_time_scale_cb,
				   GINT_TO_POINTER(start ? TRUE : FALSE));
  gtk_adjustment_set_value(adj, f);
  gtk_signal_handler_unblock_by_func(GTK_OBJECT(adj), pit_time_scale_cb,
				     GINT_TO_POINTER(start ? TRUE : FALSE));

  if (start)
    {
      if (adj_other->value < adj->value)
	{
	  adj_other->value = adj->value;
	  gtk_adjustment_value_changed(adj_other);
	}      
    }
  else
    {
      if (adj_other->value > adj->value)
	{
	  adj_other->value = adj->value;
	  gtk_adjustment_value_changed(adj_other);
	}      
    }
}


LND_TPM_NavMode   
nd_trace_area_dialog_get_mode(void)
{
  GtkNotebook *w;

  if (!trace_area_dialog)
    return LND_TPM_ERROR;

  ND_GTK_GET(w, trace_area_dialog, "nav_notebook");
  
  if (gtk_notebook_get_current_page(w) == 0)
    return LND_TPM_TIME;

  return LND_TPM_SPACE;
}


void             
nd_trace_area_dialog_set_current_part(void)
{
  LND_Trace *trace;
  LND_TraceArea area;

  return_if_no_current_trace(trace);

  if (!trace->tpm->current)
    return;

  libnd_trace_area_init_time(&area,
			     &trace->tpm->current->start_ts,
			     &trace->tpm->current->end_ts);

  nd_trace_area_dialog_sync(&area);
}


void             
nd_trace_area_dialog_sync(LND_TraceArea *area)
{
  struct bpf_timeval  delta, tv_start, tv_end;
  LND_Trace          *trace;
  double              time_start, time_end, space_start, space_end;
  GtkWidget          *w;
  GtkAdjustment      *adj;
  char                s[MAXPATHLEN];
  LND_TraceLoc        start_loc, end_loc;
  off_t               offset;

  if (!trace_area_dialog)
    return;
  
  return_if_no_current_trace(trace);

  /* First, set all the help labels at the top of the dialog */
  
  ND_GTK_GET(w, trace_area_dialog, "nav_start_epoch_label");
  g_snprintf(s, MAXPATHLEN, "%lu.%lu",
	     trace->tpm->base->start_ts.tv_sec,
	     trace->tpm->base->start_ts.tv_usec);
  gtk_label_set_text(GTK_LABEL(w), s);

  ND_GTK_GET(w, trace_area_dialog, "nav_end_epoch_label");
  g_snprintf(s, MAXPATHLEN, "%lu.%lu",
	     (unsigned long) trace->tpm->base->end_ts.tv_sec,
	     (unsigned long) trace->tpm->base->end_ts.tv_usec);
  gtk_label_set_text(GTK_LABEL(w), s);

  pcapnav_timeval_sub(&trace->tpm->base->end_ts,
		      &trace->tpm->base->start_ts,
		      &delta);

  ND_GTK_GET(w, trace_area_dialog, "nav_span_epoch_label");
  g_snprintf(s, MAXPATHLEN, "%lu.%lu",
	     (unsigned long) delta.tv_sec,
	     (unsigned long) delta.tv_usec);
  gtk_label_set_text(GTK_LABEL(w), s);

  ND_GTK_GET(w, trace_area_dialog, "nav_start_time_label");
  libnd_misc_ctime(&trace->tpm->base->start_ts, s, MAXPATHLEN, TRUE, FALSE);
  s[strlen(s)-1] = '\0';
  gtk_label_set_text(GTK_LABEL(w), s);

  ND_GTK_GET(w, trace_area_dialog, "nav_end_time_label");
  libnd_misc_ctime(&trace->tpm->base->end_ts, s, MAXPATHLEN, TRUE, FALSE);
  s[strlen(s)-1] = '\0';
  gtk_label_set_text(GTK_LABEL(w), s);

  ND_GTK_GET(w, trace_area_dialog, "nav_span_time_label");
  g_snprintf(s, MAXPATHLEN, "%s", libnd_misc_timeval_to_string(&delta));
  gtk_label_set_text(GTK_LABEL(w), s);

  /* Now update the actual sliders, spinbuttons etc. */

  /* If no area was given, try to update the selected one. */
  if (!area)
    area = trace_area_dialog_get_current_area();
    
  if (!area)
    return;

  nd_trace_area_dialog_set_mode(area->mode);

  if (! libnd_tpm_find_locs(trace->tpm, area, &start_loc, &end_loc))
    {
      D(("Area calculation failed.\n"));
      return;
    }

  D(("Area start: %p %lu, end: %p %lu\n",
     start_loc.tp, start_loc.offset,
     end_loc.tp, end_loc.offset));

  offset = libnd_tpm_map_loc_to_offset(trace->tpm, &start_loc);
  space_start = libnd_tpm_get_space_fraction(trace->tpm, offset);

  offset = libnd_tpm_map_loc_to_offset(trace->tpm, &end_loc);
  space_end = libnd_tpm_get_space_fraction(trace->tpm, offset);

  libnd_tpm_map_loc_to_timestamp(trace->tpm, &start_loc, &tv_start);
  time_start = libnd_tpm_get_time_fraction(trace->tpm, &tv_start);
  D(("Start timestamp: %lu.%lu, frac: %f\n", tv_start.tv_sec, tv_start.tv_usec, time_start));

  libnd_tpm_map_loc_to_timestamp(trace->tpm, &end_loc, &tv_end);
  time_end = libnd_tpm_get_time_fraction(trace->tpm, &tv_end);
  D(("End timestamp: %lu.%lu, frac: %f\n", tv_end.tv_sec, tv_end.tv_usec, time_end));

  /* Set scrollbar handles in space tab: */

  ND_GTK_GET(w, trace_area_dialog, "nav_frac_start_hscale");
  adj = gtk_range_get_adjustment(GTK_RANGE(w));
  adj->value = space_start;
  gtk_adjustment_value_changed(adj);

  ND_GTK_GET(w, trace_area_dialog, "nav_frac_end_hscale");
  adj = gtk_range_get_adjustment(GTK_RANGE(w));
  adj->value = space_end;
  gtk_adjustment_value_changed(adj);

  /* Set editable areas in time tab: */

  g_snprintf(s, MAXPATHLEN, "%lu", (unsigned long) tv_start.tv_sec);
  ND_GTK_GET(w, trace_area_dialog, "nav_time_start_sec_entry");
  gtk_entry_set_text(GTK_ENTRY(w), s);
  g_snprintf(s, MAXPATHLEN, "%lu", (unsigned long) tv_end.tv_sec);
  ND_GTK_GET(w, trace_area_dialog, "nav_time_end_sec_entry");
  gtk_entry_set_text(GTK_ENTRY(w), s);

  g_snprintf(s, MAXPATHLEN, "%lu", (unsigned long) tv_start.tv_usec);
  ND_GTK_GET(w, trace_area_dialog, "nav_time_start_usec_entry");
  gtk_entry_set_text(GTK_ENTRY(w), s);
  g_snprintf(s, MAXPATHLEN, "%lu", (unsigned long) tv_end.tv_usec);
  ND_GTK_GET(w, trace_area_dialog, "nav_time_end_usec_entry");
  gtk_entry_set_text(GTK_ENTRY(w), s);

  /* Set the non-editable timestamp fields in the time tab */

  libnd_misc_ctime(&tv_start, s, MAXPATHLEN, TRUE, TRUE);
  ND_GTK_GET(w, trace_area_dialog, "nav_time_start_label");
  gtk_label_set_text(GTK_LABEL(w), s);
  libnd_misc_ctime(&tv_end, s, MAXPATHLEN, TRUE, TRUE);
  ND_GTK_GET(w, trace_area_dialog, "nav_time_end_label");
  gtk_label_set_text(GTK_LABEL(w), s);

  /* Set the time scrollbar handles in the time tab */

  ND_GTK_GET(w, trace_area_dialog, "nav_time_start_hscale");
  adj = gtk_range_get_adjustment(GTK_RANGE(w));
  adj->value = time_start;
  gtk_adjustment_value_changed(adj);
  
  ND_GTK_GET(w, trace_area_dialog, "nav_time_end_hscale");
  adj = gtk_range_get_adjustment(GTK_RANGE(w));
  adj->value = time_end;
  gtk_adjustment_value_changed(adj);
}


static void
trace_area_dialog_new_area_ok(const char *area_name, void *user_data)
{
  static int counter;

  LND_TraceArea *area;
  GtkWidget *li, *w;
  GList *l, *items = NULL;
  int index;
  char *name;

  ND_GTK_GET(w, trace_area_dialog, "area_list");

  /* Make sure that we don't have duplicate area names */
  for (l = GTK_LIST(w)->children; l; l = g_list_next(l))
    {
      gtk_label_get(GTK_LABEL(GTK_BIN(l->data)->child), &name);

      if (!strcmp(area_name, name))
	{
	  char new_name[MAXPATHLEN];
	  g_snprintf(new_name, MAXPATHLEN, "%s-%i", area_name, ++counter);
	  area_name = new_name;
	  break;
	}
    }
  
  if (! (area = g_new0(LND_TraceArea, 1)))
    {
      D(("Out of memory.\n"));
      return;
    }

  D(("Creating area named '%s'\n", area_name));
  libnd_trace_area_init_space(area, 0, 1);

  li = gtk_list_item_new_with_label(area_name);
  gtk_widget_show(li);
  gtk_object_set_data(GTK_OBJECT(li), "trace_area", area);
  items = g_list_prepend(items, li);
  
  gtk_list_append_items(GTK_LIST(w), items);
  index = g_list_length(GTK_LIST(w)->children);

  /* Now that we have at least one item, enable the del/set
   * buttons.
   */
  ND_GTK_GET(w, trace_area_dialog, "trace_area_del_button");
  gtk_widget_set_sensitive(w, TRUE);
  ND_GTK_GET(w, trace_area_dialog, "trace_area_set_button");
  gtk_widget_set_sensitive(w, TRUE);

  /* Select our new item: */
  ND_GTK_GET(w, trace_area_dialog, "area_list");
  gtk_list_select_item(GTK_LIST(w), index - 1);

  return;
  TOUCH(user_data);
}


void             
nd_trace_area_dialog_new_area(void)
{
  if (!trace_area_dialog)
    return;

  nd_dialog_string(_("New Trace Area"),
		   _("Please enter a name for the new trace area:"),
		   trace_area_dialog_new_area_ok, NULL, NULL);
}


void             
nd_trace_area_dialog_del_area(void)
{
  GtkWidget *list, *w;
  GList *l;
  int index, length;

  if (!trace_area_dialog)
    return;

  ND_GTK_GET(list, trace_area_dialog, "area_list");

  if (!GTK_LIST(list)->selection)
    return;

  for (l = GTK_LIST(list)->selection; l; l = g_list_next(l))
    {
      LND_TraceArea *area;

      ND_GTK_GET(area, l->data, "trace_area");
      g_free(area);
    }

  l = NULL;
  l = g_list_append(l, GTK_LIST(list)->selection->data);
  index = gtk_list_child_position(GTK_LIST(list), GTK_LIST(list)->selection->data);

  gtk_list_remove_items(GTK_LIST(list), l);
  
  /* Check if there are no trace areas left! */
  if (!GTK_LIST(list)->children)
    {
      ND_GTK_GET(w, trace_area_dialog, "config_frame");
      gtk_widget_set_sensitive(w, FALSE);
      ND_GTK_GET(w, trace_area_dialog, "trace_area_del_button");
      gtk_widget_set_sensitive(w, FALSE);
      ND_GTK_GET(w, trace_area_dialog, "trace_area_set_button");
      gtk_widget_set_sensitive(w, FALSE);
    }
  else
    {
      /* Just select somthing else */
      length = g_list_length(GTK_LIST(list)->children);
      index = MIN(length - 1, index);
      gtk_list_select_item(GTK_LIST(list), index);
    }
}


void             
nd_trace_area_dialog_set_area(void)
{
  LND_TraceArea *area = trace_area_dialog_get_current_area();
  LND_Trace *trace;

  if (!area)
    return;
  
  return_if_no_current_trace(trace);

  libnd_trace_set_area(trace, area);
}


void             
nd_trace_area_dialog_select_area(LND_TraceArea *area)
{
  GtkWidget *w;

  if (!area || !trace_area_dialog) 
    return;

  ND_GTK_GET(w, trace_area_dialog, "config_frame");
  gtk_widget_set_sensitive(w, TRUE);
  
  nd_trace_area_dialog_sync(area);
}


syntax highlighted by Code2HTML, v. 0.9.1