/*
   Copyright (C) 1998,1999,2000,2001
   T. Scott Dattalo and Ralf Forsberg

This file is part of gpsim.

gpsim 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, or (at your option)
any later version.

gpsim 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 gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include "../config.h"
#ifdef HAVE_GUI

#include <unistd.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <glib.h>
#include <string.h>

#include <assert.h>

#include "../src/interface.h"
#include "../src/trace.h"

#include "gui.h"
#include "gui_trace.h"

#define MAXTRACES  100
#define TRACE_COLUMNS    2

typedef enum {
    MENU_BREAK_CLEAR,
    MENU_BREAK_READ,
    MENU_BREAK_WRITE,
    MENU_BREAK_READ_VALUE,
    MENU_BREAK_WRITE_VALUE,
    MENU_ADD_WATCH,
} menu_id;

static char *trace_titles[TRACE_COLUMNS]={"Cycle", "Trace"};

// gui trace flags:
#define GTF_ENABLE_XREF_UPDATES    (1<<0)

guint64 row_to_cycle[MAXTRACES];


static GtkStyle *normal_style;

//struct TraceMapping trace_map[MAXTRACES];


//========================================================================

class TraceXREF : public CrossReferenceToGUI
{
public:

  /*****************************************************************
   * Update
   *
   * This is called by the simulator when it has been determined that
   * that the trace buffer has changed and needs to be updated
   */

  void Update(int new_value)
  {

#define TRACE_STRING 100

    GtkCList *clist;

    char cycle_string[TRACE_STRING];
    char trace_string[TRACE_STRING];
    char *entry[TRACE_COLUMNS]={cycle_string,trace_string};

    Trace_Window *tw  = (Trace_Window *) (parent_window);

    if(!tw  || !tw->enabled)
      return;

    if(!tw->gp || !tw->gp->cpu)
      {
	puts("Warning gp or gp->cpu == NULL in TraceWindow_update");
	return;
      }

    // If we're not allowing xref updates then exit
    if( !(tw->trace_flags & GTF_ENABLE_XREF_UPDATES))
      return;

    strncpy(trace_string,get_trace().string_buffer,TRACE_STRING);

    if(trace_string[0] && (get_trace().string_cycle>=tw->last_cycle)) {
      tw->last_cycle = get_trace().string_cycle;
      tw->trace_map[tw->trace_map_index].cycle = get_trace().string_cycle;
      tw->trace_map[tw->trace_map_index].simulation_trace_index = get_trace().string_index;

      // Advance the trace_map_index using rollover arithmetic
      if(++tw->trace_map_index >= MAXTRACES)
	tw->trace_map_index = 0;

      clist=GTK_CLIST(tw->trace_clist);

      sprintf(cycle_string,"0x%016" PRINTF_INT64_MODIFIER "x", get_trace().string_cycle);

      gtk_clist_append  (clist, entry);
      
      if(clist->rows>MAXTRACES)
        gtk_clist_remove(clist,0);

    }

  }

};

//========================================================================

/*****************************************************************
 * TraceWindow_update
 *
 * The purpose of this routine is to refresh the trace window with
 * the latest trace information. The current pic simulation cycle (should
 * this be change to real time???) is examined and compared to what
 * is currently displayed in the trace window. If the info in the 
 * trace window is really old, this the entire window is deleted and
 * the trace is redrawn with the latest. If the trace window is rather
 * recent then the older trace info is deleted and the new is appended
 * to the end.
 *
 * INPUTS: *tw a pointer to a Trace_Window structure.
 */

void Trace_Window::Update(void)
{

  //guint64 cycle;

  if(!enabled)
    return;

  if(!gp || !gp->cpu)
  {
      puts("Warning gp or gp->cpu == NULL in TraceWindow_update");
      return;
  }

  // Get a convenient pointer to the gtk_clist that the trace is in.
  trace_clist=GTK_CLIST(trace_clist);

  gtk_clist_freeze(trace_clist);

  trace_flags |= GTF_ENABLE_XREF_UPDATES;
  if(get_cycles().value-last_cycle>=MAXTRACES)
    // redraw the whole thing
    get_trace().dump(MAXTRACES, 0);
  else 
    get_trace().dump(get_cycles().value-last_cycle, 0);


  trace_flags &= ~GTF_ENABLE_XREF_UPDATES;
  last_cycle = get_cycles().value;
  gtk_clist_thaw(trace_clist);

}


/*****************************************************************
 * TraceWindow_new_processor
 *
 * 
 */

void Trace_Window::NewProcessor(GUI_Processor *_gp)
{

#define NAME_SIZE 32

  TraceXREF *cross_reference;

  if(!gp)
    return;

  if(!enabled)
    return;
    
  cross_reference = new TraceXREF();
  cross_reference->parent_window_type =  WT_trace_window;
  cross_reference->parent_window = (gpointer) this;
  cross_reference->data = 0;
  if(get_trace().xref)
    get_trace().xref->_add((gpointer) cross_reference);

}

static int delete_event(GtkWidget *widget,
			GdkEvent  *event,
                        Trace_Window *rw)
{
  rw->ChangeView(VIEW_HIDE);
  return TRUE;
}

void Trace_Window::Build(void)
{
  if(bIsBuilt)
    return;
  GtkWidget *main_vbox;
  GtkWidget *scrolled_window;
    
  gint i;
  gint column_width,char_width;

  window=gtk_window_new(GTK_WINDOW_TOPLEVEL);

  main_vbox=gtk_vbox_new(FALSE,1);
  gtk_container_set_border_width(GTK_CONTAINER(main_vbox),0); 
  gtk_container_add(GTK_CONTAINER(window), main_vbox);
  gtk_widget_show(main_vbox);

  gtk_window_set_title(GTK_WINDOW(window), "trace viewer");

  // Trace clist
  trace_clist=GTK_CLIST(gtk_clist_new_with_titles(TRACE_COLUMNS,trace_titles));
  gtk_clist_set_column_auto_resize(trace_clist,0,TRUE);

  GTK_WIDGET_UNSET_FLAGS(trace_clist,GTK_CAN_DEFAULT);
    
  gtk_window_set_default_size(GTK_WINDOW(window), width,height);
  gtk_widget_set_uposition(GTK_WIDGET(window),x,y);
  gtk_window_set_wmclass(GTK_WINDOW(window),name(),"Gpsim");


  gtk_signal_connect(GTK_OBJECT (window), "delete_event",
		     GTK_SIGNAL_FUNC(delete_event), this);

  scrolled_window=gtk_scrolled_window_new(0, 0);

  gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(trace_clist));
  
  gtk_widget_show(GTK_WIDGET(trace_clist));
  gtk_widget_show(scrolled_window);

  gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
  ///////////////////////////////////////////////////
  ///////////////////////////////////////////////////



  normal_style = gtk_style_new ();
#if GTK_MAJOR_VERSION >= 2
  char_width = gdk_string_width(gtk_style_get_font(normal_style), "9");
#else
  char_width = gdk_string_width(normal_style->font, "9");
#endif
  column_width = 3 * char_width + 6;

  gtk_signal_connect_after(GTK_OBJECT(window), "configure_event",
  			   GTK_SIGNAL_FUNC(gui_object_configure_event),this);



  gtk_widget_show (window);

  if(!trace_map) { 
    trace_map = (struct TraceMapping *)malloc(MAXTRACES * sizeof(struct TraceMapping));
    
    for(i=0; i<MAXTRACES; i++) {
      trace_map[i].cycle = 0;
      trace_map[i].simulation_trace_index = 0;
    }
    trace_map_index = 0;
  }

  enabled=1;
  bIsBuilt = true;
  last_cycle = 0;

  NewProcessor(gp);

  Update();
  UpdateMenuItem();

}

//------------------------------------------------------------------------
// 
//
//


Trace_Window::Trace_Window(GUI_Processor *_gp)
{

  menu = "<main>/Windows/Trace";

  gp = _gp;
  set_name("trace");
  window = 0;
  wc = WC_data;
  wt = WT_trace_window;
  trace_map = 0;

  trace_flags = 0;

  get_config();

  if(enabled)
      Build();
}

#endif // HAVE_GUI


syntax highlighted by Code2HTML, v. 0.9.1