/*
   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 <gtkextra/gtksheet.h>

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

#include "gui.h"
#include "preferences.h"
#include "gui_register.h"
#include "gui_statusbar.h"
#include "gui_regwin.h"
#include "gui_watch.h"


#define TRACE_FILE_FORMAT_ASCII 0
#define TRACE_FILE_FORMAT_LXT 1

extern int gui_question(char *question, char *a, char *b);


// extern GUI_Processor *gp;

typedef enum {
    MENU_BREAK_CLEAR,
    MENU_BREAK_READ,
    MENU_BREAK_WRITE,
    MENU_BREAK_READ_VALUE,
    MENU_BREAK_WRITE_VALUE,
    MENU_ADD_WATCH,
    MENU_SETTINGS,
    MENU_LOG_SETTINGS,
    MENU_LOG_READ,
    MENU_LOG_WRITE,
    MENU_LOG_READ_VALUE,
    MENU_LOG_WRITE_VALUE,
    MENU_REGWIN_REFRESH,
} menu_id;


/////////////////////
/////
/////  Experimental code to test an object oriented way of implementing menus
/////
/////////////////////
class MenuItem {

private:
  char *name;
  menu_id id;

public:

  virtual void execute()=0;

};

class RegWindowMenuItem : public MenuItem {

private:
 Register_Window *rw;

};

class Menu_BreakClear : public RegWindowMenuItem {
public:
  virtual void execute() {
    printf("BreakClear");
  }

} BreakClear;

MenuItem *__menu_items[] = {
  &BreakClear
};

/////////////////////
/////
/////  End of experimental code
/////
/////////////////////


typedef struct _menu_item {
    char *name;
    menu_id id;
} menu_item;

static menu_item menu_items[] = {
    {"Clear breakpoints", MENU_BREAK_CLEAR},
    {"Set break on read", MENU_BREAK_READ},
    {"Set break on write", MENU_BREAK_WRITE},
    {"Set break on read value...", MENU_BREAK_READ_VALUE},
    {"Set break on write value...", MENU_BREAK_WRITE_VALUE},
    {"Set log settings...", MENU_LOG_SETTINGS},
    {"Set log on read", MENU_LOG_READ},
    {"Set log on write", MENU_LOG_WRITE},
    {"Set log on read value...", MENU_LOG_READ_VALUE},
    {"Set log on write value...", MENU_LOG_WRITE_VALUE},
    {"Add watch", MENU_ADD_WATCH},
    {"Refresh", MENU_REGWIN_REFRESH},
    {"Settings...", MENU_SETTINGS}
};

extern int font_dialog_browse(GtkWidget *w, gpointer user_data);
static int dlg_x=200, dlg_y=200;


// Used only in popup menus
Register_Window *popup_rw;

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

//--------------------------------------------------
// get_register
// get the "real" register. If 'bTopLevelOnly' is true
// 
Register *GUIRegister::get_register()
{
  if(!rma)
    return 0;

  return rma->get_register(address);
}

void GUIRegister::put_value(unsigned int new_value)
{

  Register *reg = get_register();

  if(reg)
    reg->put_value(new_value);

  // Shadow a copy of the register value so that we can tell if it has changed
  // when we go to perform an update in the future.

  shadow = reg->getRV_notrace();
}

void GUIRegister::put_shadow(RegisterValue new_value)
{
  // Update the shadow copy of the register without updating the register.
  shadow = new_value;
}

unsigned int GUIRegister::get_value()
{
  Register *reg = get_register();

  if(reg)
    return reg->get_value();

  return 0;
}


RegisterValue GUIRegister::getRV()
{
  Register *reg = get_register();

  if(reg)
    return reg->getRV_notrace();

  return RegisterValue(0,0);
}

char * GUIRegister::getValueAsString(char *str, int len, char *pFormat,
                                     RegisterValue value)
{

  if(!str || !len)
    return 0;

  if(bIsValid()) {

    char hex2ascii[] = "0123456789ABCDEF";
    int i;
    int min = (len < register_size*2) ? len : register_size*2;

    if(value.data == INVALID_VALUE)
      value.init = 0xfffffff;

    for(i=0; i < min; i++) {
      if(value.init & 0x0f)
        str[min-i-1] = '?';
      else
        str[min-i-1] = hex2ascii[value.data & 0x0f];
      value >>= 4;
    }
    str[min] = 0;

  } else
    *str = 0;

  return str;

}

bool GUIRegister::hasChanged(RegisterValue &current_value) const
{
  return (shadow != current_value);
}

void GUIRegister::Clear_xref()
{
  Register *reg = get_register();

  if(reg)
    reg->remove_xref((gpointer *)xref);
}

void GUIRegister::Assign_xref(CrossReferenceToGUI *new_xref)
{

  Register *reg = get_register();

  if(reg)
    reg->add_xref( (gpointer *)new_xref);

  xref = new_xref;
}

bool GUIRegister::hasBreak()
{

  if(rma)
    return rma->hasBreak(address);

  return false;
}

char *GUIRegister::name()
{

  static char buffer[128];

  Register *reg = get_register();
  if (!reg) {
    sprintf(buffer,"NULL");
    return buffer;
  }

  register_symbol * pRegSym = get_symbol_table().findRegisterSymbol(
    reg->address);

  if(!reg || reg->isa()==Register::INVALID_REGISTER)
    return 0;

  if(bIsAliased) {
    sprintf(buffer,"alias (%s)", reg->name().c_str());
  }
  else {
    if(pRegSym == 0) {
      strcpy(buffer,reg->name().c_str());
    }
    else {
      strcpy(buffer,pRegSym->name().c_str());
    }
  }
  return buffer;
}

bool GUIRegister::bIsValid()
{
  if(rma && (*rma)[address].getReg())
    return true;
  return false;
}

bool GUIRegister::bIsSFR()
{
  if(rma && (*rma)[address].isa() == Register::SFR_REGISTER)
    return true;
  return false;
}

GUIRegister:: GUIRegister()
{
  rma = 0;
  xref = 0;
}

GUIRegister::~GUIRegister()
{
  rma = 0;
  if(xref != NULL) {
    delete xref;
  }
}

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

class RegisterWindowXREF : public CrossReferenceToGUI
{
public:

  void Update(int new_value)
  {
    GUIRegister *reg;
    Register_Window *rw;
    int address;

  
    reg = (GUIRegister *) (data);
    rw  = (Register_Window *) (parent_window);

    if(reg->row > GTK_SHEET(rw->register_sheet)->maxrow)
      {
      puts("Warning reg->row > maxrow in xref_update_cell");
      return;
      }

    address = rw->row_to_address[reg->row]+reg->col;

    rw->registers->Get(address)->bUpdateFull=true;
    rw->UpdateRegisterCell(address);
  
    rw->UpdateASCII(reg->row);
  }

};


//========================================================================
//
// Create a class for an invalid register and instantiate a single instance
// of it. The purpose of this is to provide a place holder for the gui
// register array.
//

class InvalidGuiRegister : public GUIRegister {
public:
  void put_value(unsigned int new_value) 
  { 
    printf("(gui_regwin)Warning: writing to invalid register\n");
  };
  unsigned int get_value() { return 0;};

  InvalidGuiRegister() {
    rma=0;
  }
private:
  void operator delete(void *ignore) {};
};

static InvalidGuiRegister THE_invalid_register;

//========================================================================
// GtkSheet extensions
//
// The gtk_sheet api does not provide access to row and column labels.
// This means we can manipulate the font the way the cells can be manipulated

#if 0 // defined but not used
static GtkSheetButton *
gtk_sheet_row_button_get(GtkSheet *sheet, gint row)
{
  g_return_val_if_fail (sheet != NULL, NULL);
  g_return_val_if_fail (GTK_IS_SHEET (sheet), NULL);

  if(row < 0 || row > sheet->maxrow) return NULL;

  return (&sheet->row[row].button);
}
#endif

static void
gtk_sheet_REALLY_set_row_height(GtkSheet *sheet, gint row, gint height)
{
  g_return_if_fail (sheet != NULL);
  g_return_if_fail (GTK_IS_SHEET (sheet));

  if (row < 0 || row > sheet->maxrow)
    return;

  sheet->row[row].height = height;
}

//========================================================================
// get_value
static void a_cb(GtkWidget *w, gpointer user_data)
{
    *(int*)user_data=TRUE;
//    gtk_main_quit();
}
static void b_cb(GtkWidget *w, gpointer user_data)
{
    *(int*)user_data=FALSE;
//    gtk_main_quit();
}
// used for reading a value from user when break on value is requested
int gui_get_value(char *prompt)
{
    static GtkWidget *dialog=0;
    static GtkWidget *label;
    static GtkWidget *entry;
    GtkWidget *button;
    GtkWidget *hbox;
    
    int retval=-1;

    int value;
    
    if(dialog==0) {
      dialog = gtk_dialog_new();
      gtk_window_set_title(GTK_WINDOW(dialog),"enter value");
      //	gtk_signal_connect(GTK_OBJECT(dialog),
      //			   "configure_event",GTK_SIGNAL_FUNC(configure_event),0);
      gtk_signal_connect_object(GTK_OBJECT(dialog),
				      "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(dialog));

      label=gtk_label_new("values can be entered in decimal, hexadecimal, and octal.\nFor example: 31 is the same as 0x1f and 037");
      gtk_widget_show(label);
      gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label,FALSE,FALSE,20);

      hbox = gtk_hbox_new(0,0);
      gtk_widget_show(hbox);
      gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox,FALSE,FALSE,20);

      button = gtk_button_new_with_label("OK");
      gtk_widget_show(button);
      gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button,
			        FALSE,FALSE,10);
      gtk_signal_connect(GTK_OBJECT(button),"clicked",
			        GTK_SIGNAL_FUNC(a_cb),(gpointer)&retval);

      button = gtk_button_new_with_label("Cancel");
      gtk_widget_show(button);
      gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button,
			        FALSE,FALSE,10);
      gtk_signal_connect(GTK_OBJECT(button),"clicked",
			        GTK_SIGNAL_FUNC(b_cb),(gpointer)&retval);

      label=gtk_label_new(prompt);
      gtk_widget_show(label);
      gtk_box_pack_start(GTK_BOX(hbox), label,
			        FALSE,FALSE, 20);

      entry=gtk_entry_new();
      gtk_widget_show(entry);
      gtk_box_pack_start(GTK_BOX(hbox), entry,FALSE,FALSE,20);
    }
    else {
      gtk_label_set_text(GTK_LABEL(label),prompt);
    }
    
//    gtk_widget_set_uposition(GTK_WIDGET(dialog),dlg_x,dlg_y);
    gtk_widget_show_now(dialog);

    gtk_grab_add(dialog);
    while(retval==-1 && GTK_WIDGET_VISIBLE(dialog))
      gtk_main_iteration();
    gtk_grab_remove(dialog);
    
    gtk_widget_hide(dialog);

    if(retval==(int)TRUE)
    {
      char *end;
      const gchar *entry_text;
      entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
      value = strtoul(entry_text,&end,0);
      if(*entry_text!='\0' && *end=='\0')
        return value;
      else
        return -1;
    }
    
    return -1;
}

// used for reading a value from user when break on value is requested
void gui_get_2values(char *prompt1, int *value1, char *prompt2, int *value2)
{
    static GtkWidget *dialog=0;
    GtkWidget *button;
    GtkWidget *hbox1, *hbox2;
    static GtkWidget *label1, *label2, *label;
    static GtkWidget *entry1, *entry2;

    int value;
    
    int retval=-1;

    if(dialog==0)
    {
	dialog = gtk_dialog_new();
	gtk_window_set_title(GTK_WINDOW(dialog),"enter values");
//	gtk_signal_connect(GTK_OBJECT(dialog),
//			   "configure_event",GTK_SIGNAL_FUNC(configure_event),0);
	gtk_signal_connect_object(GTK_OBJECT(dialog),
				  "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(dialog));

	label=gtk_label_new("values can be entered in decimal, hexadecimal, and octal.\nFor example: 31 is the same as 0x1f and 037");
	gtk_widget_show(label);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label,FALSE,FALSE,20);
	
	button = gtk_button_new_with_label("OK");
	gtk_widget_show(button);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button,
			   FALSE,FALSE,10);
	gtk_signal_connect(GTK_OBJECT(button),"clicked",
			   GTK_SIGNAL_FUNC(a_cb),(gpointer)&retval);
	
	button = gtk_button_new_with_label("Cancel");
	gtk_widget_show(button);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button,
			   FALSE,FALSE,10);
	gtk_signal_connect(GTK_OBJECT(button),"clicked",
			   GTK_SIGNAL_FUNC(b_cb),(gpointer)&retval);

        // Value 1
	hbox1 = gtk_hbox_new(0,0);
	gtk_widget_show(hbox1);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox1,FALSE,FALSE,20);

	label1=gtk_label_new(prompt1);
	gtk_widget_show(label1);
	gtk_box_pack_start(GTK_BOX(hbox1), label1,
			   FALSE,FALSE, 20);

	entry1=gtk_entry_new();
	gtk_widget_show(entry1);
	gtk_box_pack_start(GTK_BOX(hbox1), entry1,FALSE,FALSE,20);

        // Value 2
	hbox2 = gtk_hbox_new(0,0);
	gtk_widget_show(hbox2);
	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox2,FALSE,FALSE,20);

	label2=gtk_label_new(prompt2);
	gtk_widget_show(label2);
	gtk_box_pack_start(GTK_BOX(hbox2), label2,
			   FALSE,FALSE, 20);

	entry2=gtk_entry_new();
	gtk_widget_show(entry2);
	gtk_box_pack_start(GTK_BOX(hbox2), entry2,FALSE,FALSE,20);

    }
    else
    {
	gtk_label_set_text(GTK_LABEL(label1),prompt1);
	gtk_label_set_text(GTK_LABEL(label2),prompt2);
    }
    
//    gtk_widget_set_uposition(GTK_WIDGET(dialog),dlg_x,dlg_y);
    gtk_widget_show_now(dialog);

    gtk_grab_add(dialog);
    while(retval==-1 && GTK_WIDGET_VISIBLE(dialog))
      gtk_main_iteration();
    gtk_grab_remove(dialog);
    
    gtk_widget_hide(dialog);

    if(retval==(int)TRUE)
    {
      // "Ok"

      char *end;
      const gchar *entry_text;

      entry_text = gtk_entry_get_text(GTK_ENTRY(entry1));
      value = strtoul(entry_text,&end,0);
      if(*entry_text=='\0' || *end!='\0')
      {
          *value1=-1;
          *value2=-1;
          return;
      }
      *value1=value;

      entry_text = gtk_entry_get_text(GTK_ENTRY(entry2));
      value = strtoul(entry_text,&end,0);
      if(*entry_text=='\0' || *end!='\0')
      {
        *value1=-1;
        *value2=-1;
        return;
      }
      *value2=value;
      return;
    }

    // "Cancel"

    *value1=-1;
    *value2=-1;
    return;
}


static const char *file_selection_name;
static int filemode;
static int fs_done;

static void
file_selection_ok (GtkWidget        *w,
		   GtkFileSelection *fs)
{
    file_selection_name=gtk_file_selection_get_filename (fs);
    fs_done=1;
}

static void
file_selection_cancel (GtkWidget        *w,
		       GtkFileSelection *fs)
{
    file_selection_name=0;
    fs_done=1;
}

static void
modepopup_activated(GtkWidget *widget, gpointer data)
{
  Dprintf((" modepopup_activated\n"));

    char *modestring;

    modestring=(char*)data;

    if(!strcmp(modestring,"ASCII"))
        filemode=TRACE_FILE_FORMAT_ASCII;
    if(!strcmp(modestring,"LXT"))
	filemode=TRACE_FILE_FORMAT_LXT;
}

static char *gui_get_log_settings(const char **filename, int *mode)
{
    static GtkWidget *window = 0;

    GtkWidget *hbox, *optionmenu, *label;

    GtkWidget *menu;
    GtkWidget *item;

    char *prompt="Log settings";

    if (!window)
    {

	window = gtk_file_selection_new (prompt);

	gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (window));

	gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE);

	gtk_signal_connect_object(GTK_OBJECT(window),
				  "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(window));
//	gtk_signal_connect_object (GTK_OBJECT (window), "destroy",
//				   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
//				   GTK_OBJECT(window));

	gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->ok_button),
			    "clicked", GTK_SIGNAL_FUNC(file_selection_ok),
			    window);
	gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->cancel_button),
			    "clicked", GTK_SIGNAL_FUNC(file_selection_cancel),
			    window);

	hbox = gtk_hbox_new(0,0);
        gtk_widget_show(hbox);

	gtk_box_pack_end(GTK_BOX(GTK_FILE_SELECTION (window)->action_area),
			 hbox,
			 FALSE,FALSE,20);

	label = gtk_label_new("File format:");
	gtk_box_pack_start(GTK_BOX(hbox),
			 label,
			 FALSE,FALSE,20);
        gtk_widget_show(label);

	optionmenu = gtk_option_menu_new();
        gtk_widget_show(optionmenu);
	gtk_box_pack_end(GTK_BOX(hbox),
			 optionmenu,
			 FALSE,FALSE,20);

	menu=gtk_menu_new();

	item=gtk_menu_item_new_with_label("ASCII");
	gtk_signal_connect(GTK_OBJECT(item),"activate",
			   (GtkSignalFunc) modepopup_activated,
			   (gpointer)"ASCII");
//      GTK_WIDGET_SET_FLAGS (item, GTK_SENSITIVE | GTK_CAN_FOCUS);
	gtk_widget_show(item);
	gtk_menu_append(GTK_MENU(menu),item);
	item=gtk_menu_item_new_with_label("LXT");
	gtk_signal_connect(GTK_OBJECT(item),"activate",
			   (GtkSignalFunc) modepopup_activated,
			   (gpointer)"LXT");
//      GTK_WIDGET_SET_FLAGS (item, GTK_SENSITIVE | GTK_CAN_FOCUS);
	gtk_widget_show(item);
	gtk_menu_append(GTK_MENU(menu),item);

        gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu);

    }

    file_selection_name=0;
    gtk_widget_show_now(window);

    fs_done=0;
    file_selection_name=0;
    gtk_grab_add(window);
    while(!fs_done && GTK_WIDGET_VISIBLE(window))
	gtk_main_iteration();
    gtk_grab_remove(window);
    
    gtk_widget_hide(window);

    if(file_selection_name==0)
    {
        *filename=0;
	return 0;
    }

    *filename=file_selection_name;
    *mode=filemode;
    return 0;
}

extern int gui_question(char *question, char *a, char *b);

// called when user has selected a menu item
static void
popup_activated(GtkWidget *widget, gpointer data)
{
  GtkSheet *sheet;

  menu_item *item;
  int i,j;
  GtkSheetRange range;
  unsigned int address;
  int value, mask;
  const char *filename;
  int mode;

  Dprintf((" popup_activated\n"));

  if(widget==0 || data==0)
    {
      printf("Warning popup_activated(%p,%p)\n",widget,data);
      return;
    }
  if(!popup_rw || !popup_rw->gp || !popup_rw->gp->cpu) {
    printf(" no cpu\n");
    return;
  }
    
  item = (menu_item *)data;
  sheet=GTK_SHEET(popup_rw->register_sheet);
  range = sheet->range;


  switch(item->id)
    {
    case MENU_BREAK_READ:
      for(j=range.row0;j<=range.rowi;j++)
	for(i=range.col0;i<=range.coli;i++)
	  {
	    address=popup_rw->row_to_address[j]+i;
	    printf("break on read \n");
	    get_bp().set_read_break(popup_rw->gp->cpu, address);
	  }
      break;
    case MENU_BREAK_WRITE:
      for(j=range.row0;j<=range.rowi;j++)
	for(i=range.col0;i<=range.coli;i++)
	  {
	    address=popup_rw->row_to_address[j]+i;
	    get_bp().set_write_break(popup_rw->gp->cpu, address);
	  }
      break;
    case MENU_BREAK_READ_VALUE:
      value = gui_get_value("value to read for breakpoint:");
      if(value<0)
	break; // Cancel
      for(j=range.row0;j<=range.rowi;j++)
	for(i=range.col0;i<=range.coli;i++)
	  {
	    address=popup_rw->row_to_address[j]+i;
	    get_bp().set_read_value_break(popup_rw->gp->cpu,address,value);
	  }
      break;
    case MENU_BREAK_WRITE_VALUE:
      value = gui_get_value("value to write for breakpoint:");
      if(value<0)
	break; // Cancel
      for(j=range.row0;j<=range.rowi;j++)
	for(i=range.col0;i<=range.coli;i++)
	  {
	    address=popup_rw->row_to_address[j]+i;
	    get_bp().set_write_value_break(popup_rw->gp->cpu,address,value);
	  }
      break;
    case MENU_BREAK_CLEAR:
      for(j=range.row0;j<=range.rowi;j++)
	for(i=range.col0;i<=range.coli;i++)
	  {
	    address=popup_rw->row_to_address[j]+i;
	    get_bp().clear_all_register(popup_rw->gp->cpu,address);
	  }
      break;
    case MENU_ADD_WATCH:
      for(j=range.row0;j<=range.rowi;j++)
	for(i=range.col0;i<=range.coli;i++)
	  {
	    address=popup_rw->row_to_address[j]+i;
	    popup_rw->gp->watch_window->Add(popup_rw->type, popup_rw->registers->Get(address));
	  }
      break;
    case MENU_SETTINGS:
      popup_rw->SettingsDialog();
      break;
    case MENU_LOG_SETTINGS:
      gui_get_log_settings(&filename, &mode);
      if(filename!=0)
        GetTraceLog().enable_logging(filename,mode);
      break;
    case MENU_LOG_READ:
      for(j=range.row0;j<=range.rowi;j++) {
        for(i=range.col0;i<=range.coli;i++)
        {
          address=popup_rw->row_to_address[j]+i;
          GetTraceLog().enable_logging();
          // FIXME the register type is ignored here (and in all other cases
          // where we're logging -- it's assumed that the register address is
          // for ram, even if in fact the user requests eeprom.
          get_bp().set_notify_read(popup_rw->gp->cpu,address);
        }
      }
      break;
    case MENU_LOG_WRITE:
      for(j=range.row0;j<=range.rowi;j++)
	for(i=range.col0;i<=range.coli;i++)
	  {
	    address=popup_rw->row_to_address[j]+i;
	    get_bp().set_notify_write(popup_rw->gp->cpu,address);
	  }
      break;
    case MENU_LOG_READ_VALUE:
      gui_get_2values("Value that the read must match for logging it:", &value,
		      "Bitmask that specifies the bits to bother about:", &mask);
      if(value<0)
	break; // Cancel
      for(j=range.row0;j<=range.rowi;j++)
	for(i=range.col0;i<=range.coli;i++)
	  {
	    address=popup_rw->row_to_address[j]+i;
	    get_bp().set_notify_read_value(popup_rw->gp->cpu,address, value, mask);

	  }
      break;
    case MENU_LOG_WRITE_VALUE:
      gui_get_2values("Value that the write must match for logging it:", &value,
		      "Bitmask that specifies the bits to bother about:", &mask);
      if(value<0)
        break; // Cancel
      for(j=range.row0;j<=range.rowi;j++)
        for(i=range.col0;i<=range.coli;i++)
          {
          address=popup_rw->row_to_address[j]+i;
          get_bp().set_notify_write_value(popup_rw->gp->cpu,address, value, mask);
          }
      break;
    case MENU_REGWIN_REFRESH:
      popup_rw->Update();
      break;
    default:
      puts("Unhandled menuitem?");
      break;
    }
}


static GtkWidget *
build_menu(Register_Window *rw)
{
  GtkWidget *menu;
  GtkWidget *item;
//  GtkAccelGroup *accel_group;
  int i;


  if(rw==0)
  {
      printf("Warning build_menu(%p)\n",rw);
      return 0;
  }
    
  menu=gtk_menu_new();


  item = gtk_tearoff_menu_item_new ();
  gtk_menu_append (GTK_MENU (menu), item);
  gtk_widget_show (item);
  
  
  for (i=0; i < (int)(sizeof(menu_items)/sizeof(menu_items[0])) ; i++){
      item=gtk_menu_item_new_with_label(menu_items[i].name);

      gtk_signal_connect(GTK_OBJECT(item),"activate",
			 (GtkSignalFunc) popup_activated,
			 &menu_items[i]);
      GTK_WIDGET_SET_FLAGS (item, GTK_SENSITIVE | GTK_CAN_FOCUS);

      if(rw->type == REGISTER_EEPROM
	 && menu_items[i].id!=MENU_ADD_WATCH
	 &&menu_items[i].id!=MENU_SETTINGS)
      {
	  GTK_WIDGET_UNSET_FLAGS (item,
				  GTK_SENSITIVE | GTK_CAN_FOCUS);
      }
      gtk_widget_show(item);
      gtk_menu_append(GTK_MENU(menu),item);
  }
  
  return menu;
}

// button press handler
static gint
do_popup(GtkWidget *widget, GdkEventButton *event, Register_Window *rw)
{

  GtkWidget *popup;
  //	GdkModifierType mods;
  GtkSheet *sheet;

  popup=rw->popup_menu;
    
  if(widget==0 || event==0 || rw==0)
    {
      printf("Warning do_popup(%p,%p,%p)\n",widget,event,rw);
      return 0;
    }
  
  sheet=GTK_SHEET(widget);

  if( (event->type == GDK_BUTTON_PRESS) &&  (event->button == 3) )
    {

      popup_rw = rw;
  
      gtk_menu_popup(GTK_MENU(popup), 0, 0, 0, 0,
		     3, event->time);
    }
  return FALSE;
}

// The following routine will convert the first number it finds in
// a string to an unsigned long integer. All of the hard work is done
// in the library function strtoul (string to unsigned long).

static unsigned long get_number_in_string(const char *number_string)
{
  unsigned long retval = 0;
  char *bad_position;
  int current_base = 16;
  
  if(number_string==0)
  {
      printf("Warning get_number_in_string(%p)\n",number_string);
      errno = EINVAL;
      return (unsigned long)-1;
  }


  errno = 0;

  retval = strtoul(number_string, &bad_position, current_base);

  if( strlen(bad_position) ) 
    errno = EINVAL;  /* string contains an invalid number */

  return(retval);
}

// when a new cell is selected, we write changes in
// previously selected cell to gpsim
// (the name of the signal seems a bit strange)
static void
set_cell(GtkWidget *widget, int row, int col, Register_Window *rw)
{
  Dprintf((" set_cell\n"));

  GtkSheet *sheet;
  const gchar *text;
  int n=0;
  //int crow, ccol;

  sheet=GTK_SHEET(widget);
  
  if(widget==0 ||
     row>sheet->maxrow || row<0 ||
     col>sheet->maxcol || col<0 || rw==0)
    {
      printf("Warning set_cell(%p,%x,%x,%p)\n",widget,row,col,rw);
      return;
    }


  GUIRegister *reg = rw->getRegister(row,col);

  
  if(!reg)
    return; // ignore user changes in ascii column for right now

  // extract value from sheet cell
  GtkWidget * sheet_entry = gtk_sheet_get_entry(sheet);
  if (!sheet_entry)
    return;

  text = gtk_entry_get_text(GTK_ENTRY(sheet_entry));

  errno = 0;
  if(text!=0 && strlen(text)>0)
    n = get_number_in_string(text);
  else
    errno = ERANGE;

  if(errno != 0)
    {
      n = reg->get_value();
      reg->put_shadow(RegisterValue(INVALID_VALUE,INVALID_VALUE));
    }

  // n is the value in the sheet cell

  if(errno != EINVAL && n != (int) reg->get_shadow().data)
    {
      reg->put_value(n & gpGuiProcessor->cpu->register_mask());
      rw->UpdateASCII(row);
    }

}

//------------------------------------------------------------------------
// int column_width(int col)
//
// Return the width of one of the register sheet columns. 
//
// Column = -1 is the row label
// Columns 0-REGISTERS_PER_ROW are ram/registers
// Column REGISTERS_PER_ROW is an ASCII string of the row data.
//
// The width of the column is based on width of a single character,
// char_width. This width is depends on the font and is computed in
// LoadStyles()
//
// FIXME: column_width assumes there are fewer than 0x1000 registers.
int Register_Window::column_width(int col)
{
  if(!char_width)
    return 0;

  // Row Labels
  if(col < 0)
    return char_width * 3;

  // Register data
  if(col < REGISTERS_PER_ROW)
    return char_width * chars_per_column;

  // ASCII column
  return char_width * (REGISTERS_PER_ROW + 1) + char_width/2;
}

//------------------------------------------------------------------------
int Register_Window::row_height(int row)
{
  return char_height ? char_height : 20;
}

//------------------------------------------------------------------------
// UpdateLabel
//
//

void Register_Window::UpdateLabel()
{
  int row = -1, col = -1;

  if(register_sheet != 0) {
    gtk_sheet_get_active_cell(register_sheet, &row, &col);

    if(col > -1 && row > -1) {
      if(col >= REGISTERS_PER_ROW)
        gtk_label_set(GTK_LABEL(location), "  ascii  ");
      else {

        GUIRegister *reg = getRegister(row,col);

        const char *n = reg ? reg->name() : "INVALID_REGISTER";

        gtk_label_set(GTK_LABEL(location), n);
      }
    }
  }
}

//------------------------------------------------------------------------
// UpdateEntry
//
//

void Register_Window::UpdateEntry()
{
  gint row, col;

  const char *text; 
  GtkWidget * sheet_entry;

  if(register_sheet != 0) {
    sheet_entry = gtk_sheet_get_entry(register_sheet);
    gtk_sheet_get_active_cell(register_sheet, &row, &col);

    if(row_to_address[row] < 0)
      return;

    GUIRegister *reg = getRegister(row,col);

    if(reg && reg->bIsValid() )
    {
      if((text=gtk_entry_get_text (GTK_ENTRY(sheet_entry))))
          gtk_entry_set_text(GTK_ENTRY(entry), text);
    }
  }
}

//------------------------------------------------------------------------
// UpdateLabelEntry
//
//

void Register_Window::UpdateLabelEntry()
{
  UpdateLabel();
  UpdateEntry();
}

static gint configure_event(GtkWidget *widget, GdkEventConfigure *e, gpointer data)
{

  if(widget->window==0)
    return 0;
    
  gdk_window_get_root_origin(widget->window,&dlg_x,&dlg_y);
  return 0; // what should be returned?, FIXME
}

//------------------------------------------------------------------------
void Register_Window::UpdateStyle()
{
  if (!register_sheet || !normalfont)
    return;

  GtkSheetRange range;

  //gtk_sheet_freeze(register_sheet);

  // Update the font for the cells
  range.row0=0;
  range.rowi=register_sheet->maxrow;
  range.col0=0;
  range.coli=register_sheet->maxcol;
  gtk_sheet_range_set_font(register_sheet, &range, normalfont);

  // Update the font for the row and column labels
  gtk_widget_modify_font(GTK_WIDGET(register_sheet),normalfont);

  // Adjust the cell sizes based on the font size
  int i;
  for(i=0; i<=register_sheet->maxcol; i++)
    gtk_sheet_set_column_width (register_sheet, i, column_width(i));
  for(i=0; i<=register_sheet->maxrow; i++)
    gtk_sheet_REALLY_set_row_height (register_sheet, i, row_height(i));


  gtk_sheet_set_row_titles_width(register_sheet, column_width(-1));
  gtk_sheet_set_column_titles_height(register_sheet, row_height(0));
  //gtk_sheet_thaw(register_sheet);


}

//------------------------------------------------------------------------
int Register_Window::LoadStyles()
{
#if GTK_MAJOR_VERSION >= 2
  normalfont = pango_font_description_from_string(normalfont_string);
#else
  normalfont=gdk_fontset_load (normalfont_string);
#endif

  if(!normalfont)
  {
    char_width = 0;
    char_height = 0;
    return 0;
  }

#if GTK_MAJOR_VERSION >= 2
  {
    PangoRectangle rect;
    PangoLayout *layout;

    layout = gtk_widget_create_pango_layout (GTK_WIDGET(register_sheet), "A");
    pango_layout_set_font_description (layout, normalfont);

    pango_layout_get_extents (layout, NULL, &rect);
    char_width =  PANGO_PIXELS(rect.width);
    char_height = PANGO_PIXELS(rect.height + (rect.height<<1))>>1;
    g_object_unref(G_OBJECT(layout));
  }

#else
  char_width = gdk_string_width (normalfont,"9");
#endif

  return 1;
}

/********************** Settings dialog ***************************/
static int settings_active;
static void settingsok_cb(GtkWidget *w, gpointer user_data)
{
  if(settings_active)
    settings_active=0;
}

int Register_Window::SettingsDialog()
{
  static GtkWidget *dialog=0;    // fixme
  GtkWidget *button;
  GtkWidget *hbox;
  static GtkWidget *normalfontstringentry;   //fixme
  GtkWidget *label;
  int fonts_ok=0;


  if(!dialog)
  {
    dialog = gtk_dialog_new();
    gtk_window_set_title (GTK_WINDOW (dialog), "Register window settings");
    gtk_signal_connect(GTK_OBJECT(dialog),
		       "configure_event",GTK_SIGNAL_FUNC(configure_event),0);
    gtk_signal_connect_object(GTK_OBJECT(dialog),
			      "delete_event",GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(dialog));


    // Normal font
    hbox = gtk_hbox_new(0,0);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox,FALSE,FALSE,20);
    gtk_widget_show(hbox);
    label=gtk_label_new("Normal font:");
    gtk_box_pack_start(GTK_BOX(hbox), label,
		       FALSE,FALSE, 20);
    gtk_widget_show(label);
    normalfontstringentry=gtk_entry_new();
    gtk_box_pack_start(GTK_BOX(hbox), normalfontstringentry,
		       TRUE, TRUE, 0);
    gtk_widget_show(normalfontstringentry);
    button = gtk_button_new_with_label("Browse...");
    gtk_widget_show(button);
    gtk_box_pack_start(GTK_BOX(hbox), button,
		       FALSE,FALSE,10);
    gtk_signal_connect(GTK_OBJECT(button),"clicked",
		       GTK_SIGNAL_FUNC(font_dialog_browse),(gpointer)normalfontstringentry);


    // OK button
    button = gtk_button_new_with_label("OK");
    gtk_widget_show(button);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button,
		       FALSE,FALSE,10);
    gtk_signal_connect(GTK_OBJECT(button),"clicked",
		       GTK_SIGNAL_FUNC(settingsok_cb),(gpointer)dialog);
  }
    
  gtk_entry_set_text(GTK_ENTRY(normalfontstringentry), normalfont_string);

  gtk_widget_set_uposition(GTK_WIDGET(dialog),dlg_x,dlg_y);
  gtk_widget_show_now(dialog);



#if GTK_MAJOR_VERSION >= 2
  PangoFontDescription *font=0;
#else
  GdkFont *font=0;
#endif

  while(fonts_ok!=1)
  {
    char fontname[256];

    settings_active=1;
    while(settings_active)
      gtk_main_iteration();

    fonts_ok=0;

    strcpy(fontname,gtk_entry_get_text(GTK_ENTRY(normalfontstringentry)));
#if GTK_MAJOR_VERSION >= 2
    if((font=pango_font_description_from_string(fontname))==0)
#else
      if((font=gdk_fontset_load(fontname))==0)
#endif
	{
	  if(gui_question("Font did not load!","Try again","Ignore/Cancel")==FALSE)
	    break;
	}
      else
	{
#if GTK_MAJOR_VERSION >= 2
#else
	  gdk_font_unref(font);
#endif
	  strcpy(normalfont_string,gtk_entry_get_text(GTK_ENTRY(normalfontstringentry)));
	  config_set_string(name(),"normalfont",normalfont_string);
	  fonts_ok++;
	}
  }

  if(!LoadStyles())
  {
    printf("no font is available\n");
    return 0;
  }

  gtk_sheet_freeze(register_sheet);
  UpdateStyle();
  gtk_sheet_thaw(register_sheet);
  gtk_widget_hide(dialog);

  return 0;
}

static gboolean
clipboard_handler(GtkWidget *widget, GdkEventKey *key)
{
  Dprintf((" clipboard_handler\n"));

  GtkSheet *sheet;

  sheet = GTK_SHEET(widget);

  if(key->state & GDK_CONTROL_MASK || key->keyval==GDK_Control_L ||
     key->keyval==GDK_Control_R){
    if((key->keyval=='c' || key->keyval == 'C') && sheet->state != GTK_STATE_NORMAL){
#if GTK_MAJOR_VERSION >= 2
      /*
        --- tsd - commented out because this function 
        is not defined in the official gtkextra-2.0 release.
      if (gtk_sheet_in_clip(sheet))
        gtk_sheet_unclip_range(sheet);
      */
#else
      if(GTK_SHEET_IN_CLIP(sheet))
        gtk_sheet_unclip_range(sheet);
#endif
      gtk_sheet_clip_range(sheet, &sheet->range);
    }
    if(key->keyval=='x' || key->keyval == 'X')
            gtk_sheet_unclip_range(sheet);    
  }
  return 0;
}

static void 
resize_handler(GtkWidget *widget, GtkSheetRange *old_range, 
                                  GtkSheetRange *new_range, 
                                  Register_Window *rw)
{
  Dprintf((" resize_handler\n"));

  int i, j, cti, ctj;
  int from, to;
    
  if(widget==0 || old_range==0 || new_range==0 || rw==0)
    {
      printf("Warning resize_handler(%p,%p,%p,%p)\n",widget,old_range,new_range,rw);
      return;
    }

  cti = new_range->coli - new_range->col0 + 1;
  ctj = new_range->rowi - new_range->row0 + 1;

  // We always copy from this one cell.
  from = rw->row_to_address[old_range->row0]+old_range->col0;
    
  for(j=0;j<ctj;j++)
    {
      for(i=0;i<cti;i++)
	{
	  to = rw->row_to_address[new_range->row0+j]+new_range->col0+i;
	  rw->registers->Get(to)->put_value(rw->registers->Get(from)->get_value());
	}
    }
}

static void 
move_handler(GtkWidget *widget, 
	     GtkSheetRange *old_range, 
	     GtkSheetRange *new_range, 
	     Register_Window *rw)
{
  Dprintf((" move_handler\n"));

  int i, j, cti, ctj;
  int from, to;

  if(!widget || !old_range || !new_range || !rw)
      return;

  cti = new_range->coli - new_range->col0 + 1;
  ctj = new_range->rowi - new_range->row0 + 1;

  for(j=0;j<ctj;j++)
    {
      for(i=0;i<cti;i++)
	{
	  from = rw->row_to_address[old_range->row0+j]+old_range->col0+i;
	  to = rw->row_to_address[new_range->row0+j]+new_range->col0+i;
	  rw->registers->Get(to)->put_value(rw->registers->Get(from)->get_value());
	}
    }
}


/* when the entry above the sheet is changed (typed a digit), we
   copy it to the cell entry */
static void
show_sheet_entry(GtkWidget *widget, Register_Window *rw)
{
  Dprintf((" show_sheet_entry\n"));

  const char *text;
  GtkSheet *sheet;
  GtkEntry *sheet_entry;

  int row,col;
 
  if(!widget || !rw)
    {
      printf("Warning show_sheet_entry(%p,%p)\n",widget,rw);
      return;
    }

  if(!GTK_WIDGET_HAS_FOCUS(widget)) return;
 
  sheet=GTK_SHEET(rw->register_sheet);
  sheet_entry = GTK_ENTRY(gtk_sheet_get_entry(sheet));

  //row=sheet->active_cell.row; col=sheet->active_cell.col;
  gtk_sheet_get_active_cell(sheet, &row, &col);

  GUIRegister *reg = rw->getRegister(row,col);

  if(reg && reg->bIsValid() )
    if((text=gtk_entry_get_text (GTK_ENTRY(rw->entry))) && sheet_entry)
      gtk_entry_set_text(sheet_entry, text);

}

/* when we have new data in the entry above the sheet, we
 copy the data to the cells/registers

 this don't get called when it is clicked
 in, only when we hit return
 */
static void
activate_sheet_entry(GtkWidget *widget, Register_Window *rw)
{
  Dprintf((" activity_sheet_entry\n"));

  GtkSheet *sheet;

  gint row, col;

  if(widget==0|| rw==0)
  {
      printf("Warning activate_sheet_entry(%p,%p)\n",widget,rw);
      return;
  }
  
  sheet=GTK_SHEET(rw->register_sheet);
  //row=sheet->active_cell.row; col=sheet->active_cell.col;
  gtk_sheet_get_active_cell(sheet, &row, &col);

  // if there are text written in the entry above the sheet, then
  // the same data is in the sheet cell (because of show_sheet_entry())

  // so we use set_cell() to write the changes from the sheet cell to gpsim
  set_cell(GTK_WIDGET(sheet),row,col,rw);
  rw->UpdateASCII(row);
      
}

/*
 we get here when the entry in a cell is changed (typed a digit), we
 copy it to the entry above the sheet.
 */
static void
show_entry(GtkWidget *widget, Register_Window *rw)
{
  Dprintf((" show_entry\n"));

    if(widget==0|| rw==0)
    {
	printf("Warning show_entry(%p,%p)\n",widget,rw);
	return;
    }
    
    if(!GTK_WIDGET_HAS_FOCUS(widget)) return;
    
    rw->UpdateEntry();

}

/* when the sheet cursor has activated a new cell, we set the
   label and entry above the sheet
 */
static gint
activate_sheet_cell(GtkWidget *widget, gint row, gint column, Register_Window *rw) 
{
  Dprintf((" activate_sheet_cell rma=%p\n",(rw? rw->rma :0)));

  GtkSheet *sheet = rw ? rw->register_sheet : 0;

  if(!sheet)
    return 0;

  if(widget==0 || row>sheet->maxrow || row<0||
     column>sheet->maxcol || column<0 || rw==0)
    {
      printf("Warning activate_sheet_cell(%p,%x,%x,%p)\n",widget,row,column,rw);
      return 0;
    }

  GUIRegister *reg = rw->getRegister(row,column);

  if(reg && reg->bIsValid() )
    // enable editing valid cells
    gtk_entry_set_editable(GTK_ENTRY(gtk_sheet_get_entry(rw->register_sheet)), 1);
  else
    // disable editing invalid cells
    gtk_entry_set_editable(GTK_ENTRY(gtk_sheet_get_entry(rw->register_sheet)), 0);

    
  rw->UpdateLabelEntry();

  return TRUE;
}

GUIRegister *Register_Window::getRegister(int row, int col)
{

  if(registers && col < REGISTERS_PER_ROW && row < MAX_ROWS) {

    int reg_address = row_to_address[row];

    if(reg_address < 0)
      return 0;

    if(reg_address+ col < MAX_REGISTERS)
      return registers->Get(reg_address+col);
  }

  return 0;

}

//-------------------------------------------------------------------
GUIRegister *Register_Window::operator [] (int address)
{

  if(!registers || address>=MAX_REGISTERS  || address<0)
    return 0;

  return registers->Get(address);
}

void Register_Window::SelectRegister(int regnumber)
{
  GtkSheetRange range;
  int row, col;
    
  if(regnumber > MAX_REGISTERS || regnumber<0) {
    printf("Warning: %s - regnumber = %x\n",__FUNCTION__,regnumber);
    return;
  }
  
  if(!gp || !gp->cpu ||!registers || !registers->Get(regnumber)) {
    printf("SelectRegister is not ready yet\n");
    return;
  }
  row=registers->Get(regnumber)->row;
  col=registers->Get(regnumber)->col;
  range.row0=range.rowi=row;
  range.col0=range.coli=col;

  gtk_sheet_select_range(GTK_SHEET(register_sheet),&range);
  if(register_sheet != NULL &&
     (GTK_SHEET(register_sheet)->view.col0>range.col0 ||
     GTK_SHEET(register_sheet)->view.coli<range.coli ||
     GTK_SHEET(register_sheet)->view.row0>range.row0 ||
      GTK_SHEET(register_sheet)->view.rowi<range.rowi))
    gtk_sheet_moveto(GTK_SHEET(register_sheet),row,col,0.5,0.5);

  UpdateLabelEntry();
    
}
void Register_Window::SelectRegister(Value *regSym)
{
  if(regSym  && typeid(*regSym) == typeid(register_symbol) &&
      register_sheet != NULL) {
    Register* pReg = (Register*)((register_symbol*)regSym)->getReg();
    SelectRegister(pReg->address);
  }

}
static void
build_entry_bar(GtkWidget *main_vbox, Register_Window *rw)
{
  Dprintf((" build_entry_bar\n"));

  GtkRequisition request; 
  GtkWidget *status_box;
  
  if(main_vbox == 0 || rw==0)
  {
      printf("Warning build_entry_bar(%p,%p)\n",main_vbox,rw);
      return;
  }
  
  status_box=gtk_hbox_new(FALSE, 1);
  gtk_container_set_border_width(GTK_CONTAINER(status_box),0);
  gtk_box_pack_start(GTK_BOX(main_vbox), status_box, FALSE, TRUE, 0);
  gtk_widget_show(status_box);

  rw->location=gtk_label_new("");
  gtk_widget_size_request(rw->location, &request);
  gtk_widget_set_usize(rw->location, 160, request.height);
  gtk_box_pack_start(GTK_BOX(status_box), rw->location, FALSE, TRUE, 0);
  GTK_WIDGET_SET_FLAGS(rw->location,GTK_CAN_DEFAULT);
  gtk_widget_show(rw->location);

  rw->entry=gtk_entry_new();
  gtk_box_pack_start(GTK_BOX(status_box), rw->entry,
		     TRUE, TRUE, 0); 
  gtk_widget_show(rw->entry);

}

void Register_Window::UpdateASCII(gint row)
{
  gint i;
  gchar name[32];

  if(row<0 || row > register_sheet->maxrow)
  {
      printf("Warning update_ascii(%x)\n",row);
      return;
  }

  if(!registers_loaded)
      return;
  
  for(i=0; i<REGISTERS_PER_ROW; i++)
  {

    name[i] = registers->Get(row_to_address[row] + i)->get_shadow().data;

    if( (name[i] < ' ') || (name[i]>'~'))
      name[i] = '.';

  }

  name[REGISTERS_PER_ROW] = 0;

  gtk_sheet_set_cell(GTK_SHEET(register_sheet), row,REGISTERS_PER_ROW, GTK_JUSTIFY_RIGHT,name);

}

gboolean Register_Window::UpdateRegisterCell(unsigned int reg_number)

{
  static gboolean bTrace = false;
  gchar name[16];

  GtkSheetRange range;
  gboolean retval=FALSE;

  if(reg_number<0 || reg_number>=MAX_REGISTERS)
  {
      printf("Warning update_register_cell(%x)\n",reg_number);
      return 0;
  }
  
  if(!enabled) 
    return 0;	   // Don't read registers when hidden. Esp with ICD.
  
  GUIRegister *guiReg = registers->Get(reg_number);

  if (!guiReg || !guiReg->rma)
    return 0;

  if(reg_number >= guiReg->rma->get_size())
    return 0;

  range.row0=guiReg->row;
  range.rowi=guiReg->row;
  range.col0=guiReg->col;
  range.coli=guiReg->col;

  // bulk mode stuff is for the ICD.
  gpsim_set_bulk_mode(1);
  RegisterValue new_value = guiReg->getRV();
  gpsim_set_bulk_mode(0);

  RegisterValue last_value=guiReg->get_shadow();

  if(bTrace)
    printf("UpdateRegisterCell() Entry: regID=%3d, Full=%s, hasChanged=%s\n",
      reg_number, guiReg->bUpdateFull ? "true " : "false",\
      guiReg->hasChanged(new_value) ? "true " : "false");
  if(guiReg->bUpdateFull) {

    // A 'Full Update' means that the foreground and background colors
    // need to be repainted.

    guiReg->bUpdateFull=false;

    if(guiReg->row<=register_sheet->maxrow) {

      guiReg->getValueAsString(name,sizeof(name),pCellFormat, new_value);
    
      gtk_sheet_set_cell(GTK_SHEET(register_sheet),
			 guiReg->row,
			 guiReg->col,
			 GTK_JUSTIFY_RIGHT,name);
    }
    // else the register is invalid and out of the register sheet
 

    //if(new_value != last_value) {
    if(guiReg->hasChanged(new_value)) {

      guiReg->put_shadow(new_value);
      guiReg->bUpdateFull=true;
      if(bTrace)
        printf("UpdateRegisterCell()    regID=3%d, bUpdateFull set to true 1\n", reg_number);
      gtk_sheet_range_set_foreground(GTK_SHEET(register_sheet), &range, gColors.item_has_changed());
    } else
      gtk_sheet_range_set_foreground(GTK_SHEET(register_sheet), &range, gColors.normal_fg());

    if(bTrace)
      printf("UpdateRegisterCell()    Background\n");
    if(guiReg->hasBreak())
      gtk_sheet_range_set_background(GTK_SHEET(register_sheet), &range, gColors.breakpoint());
    else if(!guiReg->bIsValid())
      gtk_sheet_range_set_background(GTK_SHEET(register_sheet), &range, gColors.invalid());
    else if(guiReg->bIsAliased)
      gtk_sheet_range_set_background(GTK_SHEET(register_sheet), &range, gColors.alias());
    else if(guiReg->bIsSFR())
      gtk_sheet_range_set_background(GTK_SHEET(register_sheet), &range, gColors.sfr_bg());
    else
      gtk_sheet_range_set_background(GTK_SHEET(register_sheet), &range, gColors.normal_bg());
   

    retval=TRUE;
  } else if(guiReg->hasChanged(new_value)) { //new_value!=last_value) {

    if(new_value.data==INVALID_VALUE) {
      
      guiReg->put_shadow(RegisterValue(INVALID_VALUE,INVALID_VALUE));
      sprintf (name, "??");
    } else {

      // the register has changed since last update
      guiReg->put_shadow(new_value);
      guiReg->getValueAsString(name,sizeof(name),pCellFormat, new_value);
      //sprintf (name, pCellFormat, new_value.data);
    }

    gtk_sheet_set_cell(GTK_SHEET(register_sheet),
		       guiReg->row,
		       guiReg->col,
		       GTK_JUSTIFY_RIGHT,name);

    guiReg->bUpdateFull=true;
    if(bTrace)
      printf("UpdateRegisterCell()    regID=3%d, bUpdateFull set to true 2\n", reg_number);
    gtk_sheet_range_set_foreground(GTK_SHEET(register_sheet), &range, gColors.item_has_changed());

    retval=TRUE;
  }

  gint row,col;
  gtk_sheet_get_active_cell(register_sheet, &row, &col);

  if((int)reg_number==(row_to_address[row]+col))
  {
    // if sheet cursor is standing on a cell that is changed, then
    // we update the entry above the sheet
    if(new_value.data!=last_value.data)
      UpdateEntry();
  }

  if(bTrace)
    printf("UpdateRegisterCell() Exit:  regID=%3d, Full=%s, hasChanged=%s, retval=%s\n",
      reg_number, guiReg->bUpdateFull
      ? "true " : "false",
    guiReg->hasChanged(new_value) ? "true " : "false", retval ? "true " : "false");
  return retval;
}


void Register_Window::Update()
{

  int address;
  bool bRowChanged;
  int j, i;

  if(!enabled)
    return;
    
  if(!GTK_WIDGET_VISIBLE(window))
    return;

  if(!registers_loaded)
    return;
  
  if(!gp || !gp->cpu || !register_sheet || !gp->cpu->isHardwareOnline()) {
    puts("Warning can't update register window");
    return;
  }

  gtk_sheet_freeze(register_sheet);
  for(j = 0; j<=GTK_SHEET(register_sheet)->maxrow; j++) {

    if(row_to_address[j]==-1)
      continue;

    bRowChanged = false;
    for(i = 0; i<REGISTERS_PER_ROW; i++) {
      address = row_to_address[j]+i;
      GUIRegister * pGuiReg = registers->Get(address);
      if(pGuiReg != &THE_invalid_register &&
        (pGuiReg->get_shadow().data!=INVALID_VALUE ||
        pGuiReg->bUpdateFull)) {

        if(UpdateRegisterCell(address) == (gboolean)true)
          bRowChanged = true;
      }
    }
    if(bRowChanged)
      UpdateASCII(j);
  }

  gtk_sheet_thaw(register_sheet);

}

//------------------------------------------------------------------------
void Register_Window::SetRegisterSize()
{
  if(gp && gp->cpu)
    register_size = gp->cpu->register_size();
  else
    register_size = 1;

  chars_per_column = 1 + 2*register_size;

  if(pCellFormat)
    free(pCellFormat);

  pCellFormat = (char *) malloc(10 * sizeof(char));
  sprintf(pCellFormat,"%%0%dx",register_size*2);

  if(register_sheet) {

    int i;
    char buffer[10];

    // Column labels
    for(i=0; i<register_sheet->maxcol; i++){
      sprintf(buffer,"%02x",i);
      gtk_sheet_column_button_add_label(register_sheet, i, buffer);
      gtk_sheet_set_column_title(register_sheet, i, buffer);
      gtk_sheet_set_column_width (register_sheet, i, column_width(i));
    }


    // ASCII column
    i = REGISTERS_PER_ROW;
    sprintf(buffer,"ASCII");
    gtk_sheet_column_button_add_label(register_sheet, i, buffer);
    gtk_sheet_set_column_title(register_sheet, i, buffer);

    gtk_sheet_set_column_width (register_sheet, i, column_width(i));
  
    gtk_sheet_set_row_titles_width(register_sheet, column_width(-1));

  }



}

GUIRegisterList::GUIRegisterList(RegisterMemoryAccess *pRMA) {
  m_pRMA = pRMA;
  unsigned int uAddress;
  unsigned int uRegisterSize;

  uRegisterSize = (pRMA->get_size() < MAX_REGISTERS) ? (pRMA->get_size()) : MAX_REGISTERS;

  for(uAddress=0; uAddress < uRegisterSize; uAddress++) {
    GUIRegister *pReg = new GUIRegister();
    pReg->rma = m_pRMA;
    pReg->address = uAddress;
    pReg->register_size = m_pRMA->get_cpu()->register_size();
    pReg->bIsAliased = (*m_pRMA)[uAddress].address != (unsigned int)uAddress;
    m_paRegisters[uAddress] = pReg;
  }
  for(;uAddress < MAX_REGISTERS; uAddress++)
    m_paRegisters[uAddress] = &THE_invalid_register;
}

GUIRegisterList::~GUIRegisterList() {
  unsigned int nRegs;
  unsigned int uAddress;

  nRegs = (m_pRMA->get_size() < MAX_REGISTERS) ? (m_pRMA->get_size()) : MAX_REGISTERS;
  for(uAddress=0; uAddress < nRegs; uAddress++) {
    delete m_paRegisters[uAddress];
  }
}

//------------------------------------------------------------------------
void Register_Window::NewProcessor(GUI_Processor *_gp)
{
  gint i,j, border_mask, border_width;
  unsigned int reg_number;
  CrossReferenceToGUI *cross_reference;
  gboolean row_created;
  GtkSheetRange range;

  if(!gp || !gp->cpu || !rma || !gp->cpu->isHardwareOnline())
    return;

  if( !enabled)
    return;
    
  if(!register_sheet){
    printf("Warning %s:%d\n",__FUNCTION__,__LINE__);
    return;
  }
      
  row_created=FALSE;
  unsigned int nRegs;
  nRegs = (rma->get_size() < MAX_REGISTERS) ? (rma->get_size()) : MAX_REGISTERS;

  if (!nRegs)
    return;

  gtk_sheet_freeze(register_sheet);
    
  j=0;
  i=0;

  gtk_sheet_REALLY_set_row_height (register_sheet, j, row_height(i));

  SetRegisterSize();


  for(reg_number=0;reg_number<nRegs;reg_number++) {
    i=reg_number%REGISTERS_PER_ROW;
	
    if(i==0 && row_created) {
      j++;
      row_created=FALSE;
    }
	
    GUIRegister *pGReg = registers->Get(reg_number);
    pGReg->row = j;
    pGReg->col = i;
    pGReg->put_shadow(RegisterValue(INVALID_VALUE,INVALID_VALUE));
    pGReg->bUpdateFull=true;

    if(pGReg->bIsValid()) {

      gpsim_set_bulk_mode(1);
      pGReg->put_shadow(registers->Get(reg_number)->getRV());
      gpsim_set_bulk_mode(0);

      /* Now create a cross-reference link that the simulator can use to
       * send information back to the gui
       */

      cross_reference = new RegisterWindowXREF();
      cross_reference->parent_window_type =   WT_register_window;
      cross_reference->parent_window = (gpointer) this;
      cross_reference->data = (gpointer) pGReg;
      pGReg->Assign_xref(cross_reference);

      if(!row_created)
      {
        char row_label[100];
        if(register_sheet->maxrow<j)
        {
          gtk_sheet_add_row(register_sheet,1);
          gtk_sheet_REALLY_set_row_height (register_sheet, j, row_height(0));
        }

        sprintf(row_label,"%x0",reg_number/REGISTERS_PER_ROW);
        gtk_sheet_row_button_add_label(register_sheet, j, row_label);
        gtk_sheet_set_row_title(register_sheet, j, row_label);

        row_to_address[j] = reg_number - reg_number%REGISTERS_PER_ROW;
        row_created=TRUE;
      }
    }
  }

  if(j < register_sheet->maxrow)
    gtk_sheet_delete_rows(register_sheet,j,register_sheet->maxrow-j);

  registers_loaded = 1;

  range.row0=0;
  range.rowi=register_sheet->maxrow;
  range.col0=0;
  range.coli=register_sheet->maxcol;

  UpdateStyle();
  border_mask = GTK_SHEET_RIGHT_BORDER |
    GTK_SHEET_LEFT_BORDER |
    GTK_SHEET_BOTTOM_BORDER |
    GTK_SHEET_TOP_BORDER;

  border_width = 1;

  gtk_sheet_range_set_border(register_sheet, &range, border_mask, border_width, 0);

  border_mask = GTK_SHEET_LEFT_BORDER;
  border_width = 3;

  range.col0=REGISTERS_PER_ROW;
  range.coli=REGISTERS_PER_ROW;

  gtk_sheet_range_set_border(register_sheet, &range, border_mask, border_width, 0);

  gtk_sheet_thaw(register_sheet);

  // set values in the sheet
  Update();
  SelectRegister(0);
}

static int show_event(GtkWidget *widget,
		      Register_Window *rw)
{
  Dprintf((" show_event\n"));

  rw->Update();
  return TRUE;
}

static int delete_event(GtkWidget *widget,
			GdkEvent  *event,
                        Register_Window *rw)
{
  Dprintf((" delete_event\n"));
  rw->ChangeView(VIEW_HIDE);
  return TRUE;
}

//------------------------------------------------------------------------
// Build
//
//

void Register_Window::Build()
{

  if(bIsBuilt)
    return;

  Dprintf((" Register_Window::Build()\n"));
  GtkWidget *main_vbox;
  GtkWidget *scrolled_window;

#define MAXROWS  (MAX_REGISTERS/REGISTERS_PER_ROW)
#define MAXCOLS  (REGISTERS_PER_ROW+1)
    
  char *fontstring;

  if(window!=0) {
    gtk_widget_destroy(window);
  }
	
  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);

  if(type==REGISTER_RAM)
  {
    register_sheet=GTK_SHEET(gtk_sheet_new(1,MAXCOLS,"gpsim Register Viewer [RAM]"));
    gtk_window_set_title(GTK_WINDOW(window), "register viewer [RAM]");
    // Add a status bar

    RAM_RegisterWindow *rrw = dynamic_cast<RAM_RegisterWindow *>(this);
    if(rrw && rrw->sbw)
      rrw->sbw->Create(main_vbox);
  }
  else
  {
    register_sheet=GTK_SHEET(gtk_sheet_new(1,MAXCOLS,"gpsim Register Viewer [EEPROM]"));
    gtk_window_set_title(GTK_WINDOW(window), "register viewer [EEPROM]");
  }
  //gtk_sheet_hide_column_titles(register_sheet);
  //gtk_sheet_hide_row_titles(register_sheet);
  /* create popupmenu */
  popup_menu=build_menu(this);

  build_entry_bar(main_vbox,this);

  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");

  /**************************** load fonts *********************************/
#if GTK_MAJOR_VERSION >= 2
#define DEFAULT_NORMALFONT "Monospace 10"
#else
#define DEFAULT_NORMALFONT "-adobe-courier-*-r-*-*-*-140-*-*-*-*-*-*"
#endif
  strcpy(normalfont_string,DEFAULT_NORMALFONT);
  if(config_get_string(name(),"normalfont",&fontstring))
      strcpy(normalfont_string,fontstring);

  while(!LoadStyles())
  {
    if(gui_question("Some fonts did not load.","Open font dialog","Try defaults")==FALSE)
      {
      strcpy(normalfont_string,DEFAULT_NORMALFONT);
      config_set_string(name(),"normalfont",normalfont_string);
      }
      else
      {
      SettingsDialog();
      }
  }
  UpdateStyle();
  gtk_signal_connect(GTK_OBJECT (window), "delete_event",
		     GTK_SIGNAL_FUNC(delete_event), this);

  gtk_signal_connect(GTK_OBJECT (window), "show",
		  GTK_SIGNAL_FUNC(show_event), this);

  scrolled_window=gtk_scrolled_window_new(0, 0);

  gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(register_sheet));
  
#if GTK_MAJOR_VERSION >= 2
  GTK_SHEET_CLIP_TEXT(register_sheet);
#else
  GTK_SHEET_SET_FLAGS(register_sheet, GTK_SHEET_CLIP_TEXT);
#endif

  gtk_widget_show(GTK_WIDGET(register_sheet));

  gtk_widget_show(scrolled_window);

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

  gtk_signal_connect(GTK_OBJECT(gtk_sheet_get_entry(GTK_SHEET(register_sheet))),
		     "changed", (GtkSignalFunc)show_entry, this);

  gtk_signal_connect(GTK_OBJECT(register_sheet),
		     "activate", (GtkSignalFunc)activate_sheet_cell,
		     this);

  gtk_signal_connect(GTK_OBJECT(entry),
		     "changed", (GtkSignalFunc)show_sheet_entry, this);

  gtk_signal_connect(GTK_OBJECT(entry),
		     "activate", (GtkSignalFunc)activate_sheet_entry,
		     this);

  gtk_signal_connect(GTK_OBJECT(register_sheet),
		     "key_press_event",
		     (GtkSignalFunc) clipboard_handler, 
		     0);

  gtk_signal_connect(GTK_OBJECT(register_sheet),
		     "resize_range",
		     (GtkSignalFunc) resize_handler, 
		     this);

  gtk_signal_connect(GTK_OBJECT(register_sheet),
		     "move_range",
		     (GtkSignalFunc) move_handler, 
		     this);
	
  gtk_signal_connect(GTK_OBJECT(register_sheet),
		     "button_press_event",
		     (GtkSignalFunc) do_popup, 
		     this);

  gtk_signal_connect(GTK_OBJECT(register_sheet),
		     "set_cell",
		     (GtkSignalFunc) set_cell,
		     this);

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

  SetRegisterSize();

  gtk_widget_show (window);

  gtk_widget_grab_default(location);
  
  //  GTKWAIT;
  
  bIsBuilt = true;
  
  NewProcessor(gp);

  UpdateMenuItem();
  Dprintf((" regwin is built\n"));
}

//------------------------------------------------------------------------
Register_Window::Register_Window()
{
  register_sheet = NULL;
  printf("WARNING: calling default constructor: %s\n",__FUNCTION__);

}

Register_Window::Register_Window(GUI_Processor *_gp)
{
  int i;

  gp = _gp;
  window = 0;
  wc = WC_data;
  wt = WT_register_window;
  pCellFormat = 0;
  char_width = 0;
  chars_per_column = 3; // assume byte-sized registers
  register_sheet = NULL;

  registers_loaded=0;
  
  registers = 0;

  for(i=0;i<MAX_REGISTERS/REGISTERS_PER_ROW;i++)
    row_to_address[i]=-1;
}


RAM_RegisterWindow::RAM_RegisterWindow(GUI_Processor *_gp) :
  Register_Window(_gp)
{
  menu = "<main>/Windows/Ram";
  type = REGISTER_RAM;

  set_name("register_viewer_ram");
  // Add a status bar
  sbw = new StatusBar_Window();

  get_config();

  if(enabled)
      Build();
}


void RAM_RegisterWindow::NewProcessor(GUI_Processor *_gp)
{

  if(!_gp || !_gp->cpu)
    return;

  rma = &_gp->cpu->rma;
  registers = _gp->m_pGUIRamRegisters;
  Dprintf((" RAM_RegisterWindow::NewProcessor rma=%p\n",rma));

  Register_Window::NewProcessor(_gp);
  if(sbw)
    sbw->NewProcessor(_gp, rma);

}

void RAM_RegisterWindow::Update()
{
  Register_Window::Update();
  if(sbw)
    sbw->Update();
}

EEPROM_RegisterWindow::EEPROM_RegisterWindow(GUI_Processor *_gp) :
  Register_Window(_gp)
{
  menu = "<main>/Windows/EEPROM";
  type = REGISTER_EEPROM;

  set_name("register_viewer_eeprom");
  
  get_config();

  if(enabled)
      Build();
}


void EEPROM_RegisterWindow::NewProcessor(GUI_Processor *_gp)
{

  if(!_gp || !_gp->cpu)
    return;

  rma = &_gp->cpu->ema;
  registers = _gp->m_pGUIEEPromRegisters;

  Dprintf((" EEPROM_RegisterWindow::NewProcessor rma=%p\n",rma));

  Register_Window::NewProcessor(_gp);
}

#endif // HAVE_GUI


syntax highlighted by Code2HTML, v. 0.9.1