/*
   Copyright (C) 2000,2001,2002
   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.  */

#define GTK_ENABLE_BROKEN

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


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <math.h>
#include <assert.h>


#include <iostream>
#include <iomanip>


#include "../src/modules.h"
#include "../src/stimuli.h"
#include "../src/pic-processor.h"
#include "../src/symbol.h"
#include "../src/stimuli.h"
#include "../src/value.h"
#include "../src/errors.h"
#include "../src/packages.h"

#include <vector>

#include "gui.h"
#include "gui_breadboard.h"

#define PINLINEWIDTH 3
#define CASELINEWIDTH 4

#define CASEOFFSET (CASELINEWIDTH/2)

#define FOORADIUS (CASELINEWIDTH) // radius of center top milling

#define LABELPAD 4 // increase this so wide lines doesn't clutter labels

static GdkColor high_output_color;
static GdkColor low_output_color;
static GdkColor black_color;
static GdkColor gray_color;

#define PINLENGTH (4*PINLINEWIDTH)

static int pinspacing = PINLENGTH;

#define LAYOUTSIZE_X 800
#define LAYOUTSIZE_Y 800

#define STRING_SIZE 128

#define ROUTE_RES (2*PINLINEWIDTH) // grid spacing

static void treeselect_module(GtkItem *item, GuiModule *p);

#define XSIZE LAYOUTSIZE_X/ROUTE_RES // grid size
#define YSIZE LAYOUTSIZE_Y/ROUTE_RES


/* If HMASK is set in board_matrix, then this position
 is unavailable for horizontal track */
#define HMASK 1 
#define VMASK 2
/*
 board matrix contains information about how a track can be routed.
 */
static unsigned char board_matrix[XSIZE][YSIZE];


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

class BreadBoardXREF : public CrossReferenceToGUI
{
public:

  void Update(int new_value)
  {
    Breadboard_Window *bbw  = (Breadboard_Window *) (parent_window);

    bbw->Update();

  }
};
//========================================================================

/* Check the flags in board_matrix to see if we are allowed to 
   route horizontally here */
static inline int allow_horiz(point &p)
{
  if(board_matrix[p.x][p.y] & HMASK)
    return FALSE;
  return TRUE;
}

/* Check the flags in board_matrix to see if we are allowed to 
   route vertically here */
static inline int allow_vert(point &p)
{
  if(board_matrix[p.x][p.y] & VMASK)
    return FALSE;

  return TRUE;
}

// Find the direction to go to get from s to e if there are no obstacles.
static inline route_direction calculate_route_direction(point s, point e)
{
    if(abs(s.x-e.x) > abs(s.y-e.y))
    {
	// Left or right
	if(s.x<e.x)
	    return R_RIGHT;
	return R_LEFT;
    }
    if(s.y<e.y)
	return R_UP;
    return R_DOWN;
}

// Return right/left/up/down if the endpoint is straight ahead in that dir.
static inline route_direction calculate_route_direction_exact(point s, point e)
{
    if(s.x!=e.x && s.y!=e.y)
        return R_NONE;
    if(s.y==e.y)
    {
	// Left or right
	if(s.x<e.x)
	    return R_RIGHT;
	return R_LEFT;
    }
    if(s.y<e.y)
	return R_UP;
    return R_DOWN;
}

// Put point p as first point in pat
static void inline prepend_point_to_path(path **pat, point p)
{
    path *new_point;
    route_direction dir=R_NONE;

/* This commented-out code were supposed to just move the closest
   point instead of adding points all the time. FIXME, bug. */
/*    int add_point=0;

    if(*pat!=0)
    {
	dir = calculate_route_direction_exact(p, (*pat)->p);
	if(dir==R_NONE)
	{
            // Both X and Y has changed.
	    add_point=1;
	}
	else if(*pat!=0 && (*pat)->next!=0)
	{
	    if((*pat)->p.x == p.x &&
	       (*pat)->next->p.x == p.x &&
	       (*pat)->dir==dir)
	    {
		// same x, just change y
		(*pat)->p.y=p.y;
	    }
	    else if((*pat)->p.y == p.y &&
		    (*pat)->next->p.y == p.y &&
		    (*pat)->dir==dir)
	    {
		// same y, just change x
		(*pat)->p.x=p.x;
	    }
	    else
	    {
                add_point=1;
	    }
	}
	else
	{
	    add_point=1;
	}
    }
    else
    {
        add_point=1;
    }

    if(add_point)*/
//    {
	// Lots and lots of mallocs, FIXME
	new_point = (path*)malloc(sizeof(path));
	new_point->p=p;
	new_point->next = *pat;
	if((*pat)!=0)
	{
	    dir = calculate_route_direction(p, (*pat)->p);
	    if((*pat)->dir==R_NONE)
		(*pat)->dir=dir;
	}
	new_point->dir=dir;
	*pat = new_point;
//    }
}

// Free all memory in pat
static void clear_path(path **pat)
{

    path *current_path, *next;

    if(*pat==0)
	return;

    current_path = *pat;

    *pat = 0;

    while(current_path!=0)
    {
	next = current_path->next;

	free(current_path);

	current_path = next;
    }
}

#if 0
/* failed attempt to remove unnessessary points in paths. FIXME bug*/
// Compress sequences with same x or y to one sequence
static void compress_path(path **pat)
{
    int x,y;

    path *current_path;

    current_path = *pat;

    x=current_path->p.x;
    y=current_path->p.y;

    while(current_path!=0)
    {
        path *next_path = current_path->next;
	path *next2_path = next_path->next;

	if(next_path==0 || next2_path==0)
            break;

	if(current_path->p.x==next_path->p.x &&
	   current_path->p.x==next2_path->p.x &&
	   current_path->dir==next_path->dir &&
	   current_path->dir==next2_path->dir)
	{
	    current_path->next=next2_path;
	    free(next_path);
            continue;
	}
	if(current_path->p.y==next_path->p.y &&
	   current_path->p.y==next2_path->p.y &&
	   current_path->dir==next_path->dir &&
	   current_path->dir==next2_path->dir)
	{
	    current_path->next=next2_path;
	    free(next_path);
            continue;
	}

	current_path = current_path->next;
    }
}
#endif

// mask_matrix is used by trace_two_poins to know where is has been, and
// how quickly it came here. (depth is stored here if lower)
static unsigned short mask_matrix[XSIZE][YSIZE];

// maxdepth is shortest path from start to end
static unsigned short maxdepth;

// Penalty for making a turn in a trace
#define turnq(a,b) (((a)!=(b))*10)

static unsigned long calls;

/*
This is an recursive routine that tries to find a path between p and end.
*/
static int trace_two_points(path **pat,   // Pointer to resulting path
			    point p,  // Where we are now
			    point end,    // Where we want to go
			    int depth,
			    route_direction lastdir)    // How deep in we are
{
    int retval;
    route_direction dir;
    point up, left, right, down;

    if(depth==0)
    {
	// Initialize mask_matrix and maxdepth on first call

	int x,y;
	// Initialize mask_matrix and maxdepth
	//maxdepth=500;
	for(x=0;x<XSIZE;x++)
	    for(y=0;y<YSIZE;y++)
		mask_matrix[x][y]=maxdepth;

	clear_path(pat);
        calls=0;
    }

    calls++;

    ////////////////////////////////////////
    // Recusion termination
    ////////////////////////////////////////
    if(depth>maxdepth)
	return FALSE;
    if(depth>mask_matrix[p.x][p.y])
	return FALSE;
    if(abs(p.x-end.x)+abs(p.y-end.y)+depth>maxdepth)
	return FALSE;
    if(p.x == end.x && p.y==end.y)
    {
	// We are at end point

	if(depth < maxdepth)
	{
	    // We found a new shortest path.

	    //printf("Found path with length %d\n",depth);

	    maxdepth = depth;
	    clear_path(pat);
	    prepend_point_to_path(pat, p);
	    return TRUE;
	}
	return FALSE;
    }

    // Store new (closer) depth in mask_matrix.
    mask_matrix[p.x][p.y]=depth;

    // Find the general direction we want to go
    dir = calculate_route_direction(p,end);

    // Recursion return value
    retval=0;

    // Convenience
    up=p;up.y++;
    down=p;down.y--;
    left=p;left.x--;
    right=p;right.x++;

    /* Depending on where we wish to go, do recursion so that we likely
     will quickly find at least some kind of path to end.

     If we don't do that maxdepth will stay large, and the traceing will
     likely take a long time.

     We use allow_vert and allow_horiz to determine if the movement is
     allowed, of if there is something in our way.

     */
    switch(dir)
    {
    case R_UP:
	if(allow_vert(up))
	    retval|=trace_two_points(pat,up,end,depth+1+turnq(lastdir,R_UP),R_UP);
	if(p.x<end.x)
	{
	    if(allow_horiz(right))
		retval|=trace_two_points(pat,right,end,depth+1+turnq(lastdir,R_RIGHT),R_RIGHT);
	    if(allow_horiz(left))
		retval|=trace_two_points(pat,left,end,depth+1+turnq(lastdir,R_LEFT),R_LEFT);
	}
	else
	{
	    if(allow_horiz(left))
		retval|=trace_two_points(pat,left,end,depth+1+turnq(lastdir,R_LEFT),R_LEFT);
	    if(allow_horiz(right))
		retval|=trace_two_points(pat,right,end,depth+1+turnq(lastdir,R_RIGHT),R_RIGHT);
	}
	if(allow_vert(down))
	    retval|=trace_two_points(pat,down,end,depth+1+turnq(lastdir,R_DOWN),R_DOWN);
	break;
    case R_DOWN:
	if(allow_vert(down))
	    retval|=trace_two_points(pat,down,end,depth+1+turnq(lastdir,R_DOWN),R_DOWN);
	if(p.x<end.x)
	{
	    if(allow_horiz(right))
		retval|=trace_two_points(pat,right,end,depth+1+turnq(lastdir,R_RIGHT),R_RIGHT);
	    if(allow_horiz(left))
		retval|=trace_two_points(pat,left,end,depth+1+turnq(lastdir,R_LEFT),R_LEFT);
            }
	else
	{
	    if(allow_horiz(left))
		retval|=trace_two_points(pat,left,end,depth+1+turnq(lastdir,R_LEFT),R_LEFT);
	    if(allow_horiz(right))
		retval|=trace_two_points(pat,right,end,depth+1+turnq(lastdir,R_RIGHT),R_RIGHT);
	}
	if(allow_vert(up))
	    retval|=trace_two_points(pat,up,end,depth+1+turnq(lastdir,R_UP),R_UP);
	break;
    case R_LEFT:
	if(allow_horiz(left))
	    retval|=trace_two_points(pat,left,end,depth+1+turnq(lastdir,R_LEFT),R_LEFT);
	if(p.y<end.y)
	{
	    if(allow_vert(up))
		retval|=trace_two_points(pat,up,end,depth+1+turnq(lastdir,R_UP),R_UP);
	    if(allow_vert(down))
		retval|=trace_two_points(pat,down,end,depth+1+turnq(lastdir,R_DOWN),R_DOWN);
	}
	else
	{
	    if(allow_vert(down))
		retval|=trace_two_points(pat,down,end,depth+1+turnq(lastdir,R_DOWN),R_DOWN);
	    if(allow_vert(up))
		retval|=trace_two_points(pat,up,end,depth+1+turnq(lastdir,R_UP),R_UP);
	}
	if(allow_horiz(right))
	    retval|=trace_two_points(pat,right,end,depth+1+turnq(lastdir,R_RIGHT),R_RIGHT);
	break;
    case R_RIGHT:
	if(allow_horiz(right))
	{
	    retval|=trace_two_points(pat,right,end,depth+1+turnq(lastdir,R_RIGHT),R_RIGHT);
	}
	if(p.y<end.y)
	{
	    if(allow_vert(up))
		retval|=trace_two_points(pat,up,end,depth+1+turnq(lastdir,R_UP),R_UP);
	    if(allow_vert(down))
		retval|=trace_two_points(pat,down,end,depth+1+turnq(lastdir,R_DOWN),R_DOWN);
	}
	else
	{
	    if(allow_vert(down))
		retval|=trace_two_points(pat,down,end,depth+1+turnq(lastdir,R_DOWN),R_DOWN);
	    if(allow_vert(up))
		retval|=trace_two_points(pat,up,end,depth+1+turnq(lastdir,R_UP),R_UP);
	}
	if(allow_horiz(left))
	{
	    retval|=trace_two_points(pat,left,end,depth+1+turnq(lastdir,R_LEFT),R_LEFT);
	}
	break;

    case R_NONE:
      break;
    }


    // Check if some of the recursive traces went well.
    if(retval==(int)TRUE)
    {
	// We found a path to end. Add point p to path.
/* bug ahead, FIXME:	if(*pat!=0 && (*pat)->next!=0)
	{
          // If there are point in pat already, then check if
            // we can use that and just change the coords there.
	    if((*pat)->p.x == p.x &&
	       (*pat)->next->p.x == p.x &&
	      (*pat)->dir==dir)
	    {
		// same x, just change y
		(*pat)->p.y=p.y;
	    }
	    else if((*pat)->p.y == p.y &&
	       (*pat)->next->p.y == p.y)
	    {
		// same y, just change x
		(*pat)->p.x=p.x;
	    }
	    else
	    {
                // This is a turn in the trace. We need another point.
		prepend_point_to_path(pat, p, dir);
	    }
	}
	else*/
	{
          // This is first or second point.
	    prepend_point_to_path(pat, p);
	}
//	if(depth==0)
//	{
//            printf("Successful trace with %ld steps\n",calls);
//	}
	return TRUE;
    }

//    if(depth==0)
//    {
//	printf("Unsuccessful trace with %ld steps\n",calls);
//    }
    return FALSE; 
}
#if 0 // enable for debug
// print huge ascii picture of the board_matrix for debugging
void print_matrix(void)
{
    int x,y;

    for(y=YSIZE-1;y>=0;y--)
    {
        for(x=0;x<XSIZE;x++)
        {
	    if(board_matrix[x][y]==0)
		putchar('.');
	    else if(board_matrix[x][y]==(HMASK|VMASK))
		putchar('X');
	    else if(board_matrix[x][y]==HMASK)
		putchar('-');
	    else if(board_matrix[x][y]==VMASK)
		putchar('|');
	    else// if(isalnum(board_matrix[x][y]))
		putchar(board_matrix[x][y]);
	    //			else
	    //				assert(0);
	}
	putchar('\r');
	putchar('\n');
    }
} 

// Debug. Draw routing constraints. FIXME draw from board_matrix instead.
static void draw_board_matrix(Breadboard_Window *bbw)
{
    int x,y, width, height;
    GList *mi;
    int i;

    // Loop all modules
    mi = bbw->modules;
    while(mi!=0)
    {
	GuiModule *p = static_cast<GuiModule*>(mi->data);
	if(p->IsBuilt()) {
        x=p->x;
	y=p->y/*-PINLENGTH*/;
	width=p->width/*+PINLENGTH*/;
	height=p->height/*+PINLENGTH*/;

        // Debug. This shows the boxes that limits traceing.
	gdk_draw_rectangle(bbw->layout_pixmap,
			   bbw->case_gc,0,
			   x,y,width,height);
	//printf("%dx%d @ %d,%d with %d pins\n",width,height,x,y,p->module->get_pin_count());

	// Draw barriers around pins so the tracker can only get in
        // straigt to the pin and not from the side.
	for(i=1;i<=p->module()->get_pin_count();i++)
	{

	    GList *e;

	    e = g_list_nth(p->pins(), i-1);

            GuiPin *gp = static_cast<GuiPin*>(e->data);

	    switch(gp->orientation)
	    {
	    case LEFT:
		y=p->y+gp->y;
		gdk_draw_line(bbw->layout_pixmap,
			      bbw->case_gc,
			      p->x+gp->x-PINLENGTH,y,
			      p->x+gp->x+gp->width,y);
		y=p->y+gp->y+gp->height;
		gdk_draw_line(bbw->layout_pixmap,
			      bbw->case_gc,
			      p->x+gp->x-PINLENGTH,y,
			      p->x+gp->x+gp->width,y);
		break;
	    case RIGHT:
		y=p->y+gp->y;
		gdk_draw_line(bbw->layout_pixmap,
			      bbw->case_gc,
			      p->x+gp->x,y,
			      p->x+gp->x+gp->width+PINLENGTH,y);
		y=p->y+gp->y+gp->height;
		gdk_draw_line(bbw->layout_pixmap,
			      bbw->case_gc,
			      p->x+gp->x,y,
			      p->x+gp->x+gp->width+PINLENGTH,y);
		break;
	    default:
                assert(0);
	    }
	}
	}
        mi=mi->next;
    }
}
#endif

static GList *nodepath_list;

// Draw nodes in nodepath_list to layout_pixmap
static void clear_nodes(Breadboard_Window *bbw)
{

    GList *iter;
    path *nodepath;

    iter = nodepath_list;
    while(iter!=0)
    {
	nodepath = (path*)iter->data;

	clear_path(&nodepath);

        nodepath_list = g_list_remove(nodepath_list, iter->data);

        iter=nodepath_list;
    }

}

static void layout_adj_changed(GtkWidget *widget, Breadboard_Window *bbw);

// Draw node in nodepath_list to layout_pixmap
static void draw_nodes(Breadboard_Window *bbw)
{

    GList *iter;

    gdk_draw_rectangle (bbw->layout_pixmap,
			bbw->window->style->bg_gc[GTK_WIDGET_STATE (bbw->window)],
			TRUE,
			0, 0,
			LAYOUTSIZE_X,
			LAYOUTSIZE_Y);

    iter = nodepath_list;

    while(iter!=0) {

      int last_x, last_y;
      path *current_path;


      path *nodepath;
      nodepath = (path*)iter->data;

      current_path = nodepath;

      last_x = current_path->p.x*ROUTE_RES;
      last_y = current_path->p.y*ROUTE_RES;

      current_path=current_path->next;

      gdk_gc_set_foreground(bbw->pinline_gc,&black_color);

      while(current_path!=0)
	{
	  int x,y;

	  x=current_path->p.x*ROUTE_RES;
	  y=current_path->p.y*ROUTE_RES;

	  gdk_draw_line(bbw->layout_pixmap,
			bbw->pinline_gc,
			last_x,last_y,
			x,y);

	  last_x=x;
	  last_y=y;

	  current_path = current_path->next;
	}
      iter=iter->next;
    }


    layout_adj_changed(0,bbw);
}



// Here we fill board_matrix with module packages, so that trace_two_points
// know not to trace over them.
static void update_board_matrix(Breadboard_Window *bbw)
{
    int x,y, width, height;
    GList *mi;
    int i;

    // Clear first.
    for(y=YSIZE-1;y>=0;y--)
    {
	for(x=0;x<XSIZE;x++)
	    board_matrix[x][y]=0;
    }

    // Mark board outline, so we limit traces here
    for(x=0;x<XSIZE;x++)
    {
	board_matrix[x][0]=(HMASK|VMASK);
	board_matrix[x][YSIZE-1]=(HMASK|VMASK);
    }
    for(y=0;y<YSIZE;y++)
    {
	board_matrix[0][y]=(HMASK|VMASK);
	board_matrix[XSIZE-1][y]=(HMASK|VMASK);
    }


    // Loop all modules, and put its package and pins to board_matrix
    mi = bbw->modules;
    while(mi!=0) {

      GuiModule *p = static_cast<GuiModule*>(mi->data);

      if(p && p->IsBuilt()) {
        x=p->x();
	y=p->y();
	width=p->width();
	height=p->height();
 
	for(y = p->y() - ROUTE_RES; 
	    y < p->y() + height + ROUTE_RES && y/ROUTE_RES < YSIZE; 
	    y += ROUTE_RES)
	{
	  for(x = p->x(); 
	      x < p->x() + width && x/ROUTE_RES<XSIZE;
	      x += ROUTE_RES)
	    	board_matrix[x/ROUTE_RES][y/ROUTE_RES]=(HMASK|VMASK);
	}

	// Draw barriers around pins so the tracker can only get in
        // straigt to the pin and not from the side.
	for(i=1;i<=p->pin_count();i++) {

	  GList *e;

	  e = g_list_nth(p->pins(), i-1);

	  GuiPin *gp = static_cast<GuiPin*>(e->data);

	  switch(gp->orientation)
	    {
	    case LEFT:
	      y = gp->y() - gp->height() / 2;
	      for(x = gp->x() -  PINLENGTH;
		  x < gp->x() + gp->width();
		  x += ROUTE_RES)
		board_matrix[x/ROUTE_RES][y/ROUTE_RES]=(HMASK|VMASK);

	      y = gp->y() + gp->height() / 2;
	      for(x = gp->x() -  PINLENGTH;
		  x < gp->x() + gp->width();
		  x += ROUTE_RES)
		board_matrix[x/ROUTE_RES][y/ROUTE_RES]=(HMASK|VMASK);
	      break;

	    case RIGHT:
	      y = gp->y() - gp->height() / 2;
	      for(x = gp->x() - PINLENGTH;
		  x < gp->x() + gp->width();
		  x += ROUTE_RES)
		board_matrix[x/ROUTE_RES][y/ROUTE_RES]=(HMASK|VMASK);
	      y = gp->y() + gp->height() / 2;
	      for(x = gp->x() - PINLENGTH;
		  x < gp->x() + gp->width();
		  x += ROUTE_RES)
		board_matrix[x/ROUTE_RES][y/ROUTE_RES]=(HMASK|VMASK);
	      break;
	    default:
	      assert(0);
	    }
	}
      }
      mi=mi->next;
    }

    clear_nodes(bbw);
    draw_nodes(bbw);
}

// Add path to board_matrix. This will make trace_two_point to not trace
// at its place. It can trace over it when in straight angle.
static void add_path_to_matrix(path *pat)
{
    int x=-1, y=-1;
    if(pat!=0)
    {
	x=pat->p.x;
	y=pat->p.y;
	pat=pat->next;
    }
    while(pat!=0)
    {
	if(pat->dir==R_LEFT || pat->dir==R_RIGHT)
	    board_matrix[x][y]|=HMASK;
	if(pat->dir==R_DOWN || pat->dir==R_UP)
	    board_matrix[x][y]|=VMASK;
	while(x!=pat->p.x || y!=pat->p.y)
	{
	    if(x<pat->p.x)
		x++;
	    if(x>pat->p.x)
		x--;
	    if(y<pat->p.y)
		y++;
	    if(y>pat->p.y)
                y--;
	    if(pat->dir==R_LEFT || pat->dir==R_RIGHT)
		board_matrix[x][y]|=HMASK;
	    if(pat->dir==R_DOWN || pat->dir==R_UP)
		board_matrix[x][y]|=VMASK;
	}

	pat = pat->next;
    }
}

static GuiPin *find_gui_pin(Breadboard_Window *bbw, stimulus *pin);

#define MAX_PATHS 32

static path *shortest_path[100][100];//[MAX_PATHS]=0;
static int pathlen[100][100];

static int *permutations;
static int *shortest_permutation;

#include <algorithm>

static void reverse_path(path **pat)
{
    path *next, *last=0;

    while(*pat != 0)
    {
	// Keep a pointer to next
	next = (*pat)->next;

	// New next poins to last (reversing the list)
	(*pat)->next = last;

	last = *pat;
	*pat = next;
    }
    *pat = last;
}

static void reverse_path_if_endpoint(point startpoint, path **pat)
{
    point pat_start, pat_end;
    int dist_start, dist_end;
    path *iter;

    iter = *pat;

    pat_start = iter->p;

    while(iter->next!=0)
        iter=iter->next;

    pat_end = iter->p;

    dist_start = abs(pat_start.x-startpoint.x) + abs(pat_start.y-startpoint.y);
    dist_end = abs(pat_end.x-startpoint.x) + abs(pat_end.y-startpoint.y);

    if(dist_start > dist_end && dist_end<5)
    {
	// Reverse the list *pat

        reverse_path(pat);
    }
}

static void reverse_path_if_startpoint(point startpoint, path **pat)
{
    point pat_start, pat_end;
    int dist_start, dist_end;
    path *iter;

    iter = *pat;

    pat_start = iter->p;

    while(iter->next!=0)
        iter=iter->next;

    pat_end = iter->p;

    dist_start = abs(pat_start.x-startpoint.x) + abs(pat_start.y-startpoint.y);
    dist_end = abs(pat_end.x-startpoint.x) + abs(pat_end.y-startpoint.y);

    if(dist_start < dist_end && dist_start<5)
    {
	// Reverse the list *pat

        reverse_path(pat);
    }
}

static void path_copy_and_cat(path **pat, path **source)
{
    path *dest, *prev=0;

    dest = *pat;

    if(dest!=0)
    {
	reverse_path_if_startpoint((*source)->p, pat);

	while(dest->next!=0)
	{
	    dest=dest->next;
	}

	reverse_path_if_endpoint(dest->p, source);

	if((abs((*source)->p.x-dest->p.x) + abs((*source)->p.y-dest->p.y)) > 5)
	{
	    puts("Assert failure");
	    printf("%d, %d\n",
		   abs((*source)->p.x-dest->p.x),
		   abs((*source)->p.y-dest->p.y));
	}

	prev = dest;
	dest=dest->next;
    }

    path *sourceiter = *source;

    while(sourceiter!=0)
    {

	dest = (path*) malloc(sizeof(path));
	memcpy(dest, sourceiter, sizeof(path));
	dest->next=0;

	if(*pat==0)
	    *pat=dest;

	if(prev!=0)
	{
	    prev->next = dest;
	}

	prev = dest;
	sourceiter=sourceiter->next;
    }
}



/*
 Trace a node, and add result to nodepath_list
 */
static void trace_node(struct gui_node *gn)
{
    GuiPin *p;
    Breadboard_Window *bbw;
    stimulus *stimulus;
    GList *pinlist=0;
    int nr_of_nodes=0;
    int i,j;
    int didnt_work=0;

    point start={-1,-1},end;

    bbw=gn->bbw;

    stimulus = gn->node->stimuli;

    // Make a glist of all gui_pins in the node
    while(stimulus!=0)
    {
	p = find_gui_pin(bbw, stimulus);

	if(p==0)
	{
	    puts("Not found");
	    g_list_free(pinlist);
            return;
	}

	pinlist = g_list_append(pinlist, p);
        nr_of_nodes++;

	stimulus=stimulus->next;
    }

    // Allocate an array of shortest_paths, indexed with 2x glist position.
//FIXME   shortest_path = (path***) malloc(nr_of_nodes*nr_of_nodes*sizeof(path*));

    permutations = (int*)malloc(sizeof(int)*nr_of_nodes);
    shortest_permutation = (int*)malloc(sizeof(int)*nr_of_nodes);
    for(i=0;i<nr_of_nodes;i++)
        permutations[i]=i;

    // Trace between all stimulus, and store the distances in the array.
    for(i=0;i<nr_of_nodes;i++)
    {
	GuiPin *pi, *pj;
        GList *li, *lj;

	li = g_list_nth(pinlist,i);
        assert(li!=0);
        pi = static_cast<GuiPin*>(li->data);

	fflush(stdout);
	for(j=i+1;j<nr_of_nodes;j++)
	{
	    lj = g_list_nth(pinlist,j);
	    assert(lj!=0);
	    pj = static_cast<GuiPin*>(lj->data);

	    start.x=pi->x()/ROUTE_RES;
	    start.y=pi->y()/ROUTE_RES;

	    end.x=pj->x()/ROUTE_RES;
	    end.y=pj->y()/ROUTE_RES;

//	    printf("Tracing from %d,%d to %d,%d\n",start.x,start.y,end.x,end.y);
	    maxdepth=abs(start.x-end.x)+abs(start.y-end.y);
	    maxdepth=maxdepth*2+100; // Twice the distance, and 5 turns
//	    printf("Trying maxdepth %d\n",maxdepth);
	    trace_two_points(&shortest_path[i][j], start, end,0,R_UP);
	    if(shortest_path[i][j]==0)
	    {
		printf("\n### Couldn't trace from pin %s to pin %s!\n",
                       pi->getIOpin()->name().c_str(),
                       pj->getIOpin()->name().c_str());
		didnt_work=1;
	    }
	    pathlen[i][j]=maxdepth;

	    pathlen[j][i]=maxdepth;
            shortest_path[j][i]=shortest_path[i][j];
	}
    }

    if(didnt_work)
    {
	printf("\n###### Couldn't trace node %s!\n",gn->node->name().c_str());
	for(i=0;i<nr_of_nodes;i++)
	    for(j=i+1;j<nr_of_nodes;j++)
		clear_path(&shortest_path[i][j]);
	free(permutations);
	free(shortest_permutation);
	g_list_free(pinlist);
	return;
    }

    // Find the combination that produces the shortest node.
    int minlen = 100000;
    do
    {
	int sum=0;

//	printf("%d ",permutations[0]);
	for(i=0;i<nr_of_nodes-1;i++)
	{
//	    printf("%d ",permutations[i+1]);
	    sum+=pathlen[permutations[i]][permutations[i+1]];
	}
//	printf("length %d\n",sum);

	if(sum < minlen)
	{
	    minlen=sum;
	    for(i=0;i<nr_of_nodes;i++)
	    {
                shortest_permutation[i]=permutations[i];
	    }
	}
        // Fixme, I'd rather use next_combination().
    } while ( next_permutation( permutations, permutations+nr_of_nodes ) );

//    printf(" : Length %d\n", minlen);
//    for(i=0;i<nr_of_nodes;i++)
//    {
//	printf("%d ",shortest_permutation[i]);
//    }
    //puts("");

    path *nodepath=0;
    for(i=0;i<nr_of_nodes-1;i++)
    {
	path_copy_and_cat(&nodepath,&shortest_path[shortest_permutation[i]][shortest_permutation[i+1]]);
    }

    for(i=0;i<nr_of_nodes;i++)
	for(j=i+1;j<nr_of_nodes;j++)
	    clear_path(&shortest_path[i][j]);
    free(permutations);
    free(shortest_permutation);

	if(nodepath!=0)
	{
//	    compress_path(&nodepath);

	    add_path_to_matrix(nodepath);

	    nodepath_list = g_list_append(nodepath_list, nodepath);
	}
}



GuiPin *find_gui_pin(Breadboard_Window *bbw, stimulus *pin)
{

  GList *iter = bbw->modules;

  while(iter) {
    
    GuiModule *m = static_cast<GuiModule *>(iter->data);

    int i;
    for(i=1;i<=m->module()->get_pin_count();i++) {

      stimulus *p;

      p=m->module()->get_pin(i);

      if(p == pin)
	{
	  GList *e;

	  e = g_list_nth(m->pins(), i-1);

	  return static_cast<GuiPin*>(e->data);
	}
    }

    iter = iter->next;
  }

  return 0;
}







static gboolean expose_pin(GtkWidget *widget,
		       GdkEventExpose *event,
		       GuiPin *p)
{

    if(p->pixmap==0)
    {
	puts("bbw.c: no pixmap1!");
	return 0;
    }

    gdk_draw_pixmap(widget->window,
		    widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
		    p->pixmap,
		    event->area.x, event->area.y,
		    event->area.x, event->area.y,
		    event->area.width, event->area.height);
    return 0;
}

static void treeselect_stimulus(GtkItem *item, GuiPin *pin)
{

    char text[STRING_SIZE];
    char string[STRING_SIZE];

    char *pText = "Not connected";
    char *pString = "Stimulus";
    if(!pin)
      return;

    gtk_widget_show(pin->bbw()->stimulus_frame);
    gtk_widget_hide(pin->bbw()->node_frame);
    gtk_widget_hide(pin->bbw()->module_frame);
    gtk_widget_hide(pin->bbw()->pic_frame);

    if(pin->getIOpin()) {
      snprintf(string,sizeof(string),"Stimulus %s",pin->getIOpin()->name().c_str());
      pString = string;

      if(pin->getSnode()!=0)
	snprintf(text,sizeof(text),"Connected to node %s", pin->getSnode()->name().c_str());
      else
	snprintf(text,sizeof(text),"Not connected");
      pText = text;
    }

    gtk_frame_set_label(GTK_FRAME(pin->bbw()->stimulus_frame),pString);
    gtk_label_set_text(GTK_LABEL(pin->bbw()->stimulus_settings_label), pText);

    pin->bbw()->selected_pin = pin;
}

static void treeselect_node(GtkItem *item, struct gui_node *gui_node)
{

    char name[STRING_SIZE];
    char *text[1];
    stimulus *stimulus;
    char str[STRING_SIZE];

    text[0]=name;

//    printf("treeselect_node %p\n",gui_node);

    if(gui_node->node!=0)
    {
	snprintf(str,sizeof(str),"Node %s",gui_node->node->name().c_str());
	gtk_frame_set_label(GTK_FRAME(gui_node->bbw->node_frame),str);

	gtk_widget_show(gui_node->bbw->node_frame);
    }
    else
    {
	gtk_widget_hide(gui_node->bbw->node_frame);
    }
    gtk_widget_hide(gui_node->bbw->stimulus_frame);
    gtk_widget_hide(gui_node->bbw->module_frame);
    gtk_widget_hide(gui_node->bbw->pic_frame);

    // Clear node_clist
    gtk_clist_clear(GTK_CLIST(gui_node->bbw->node_clist));

    if(gui_node->node!=0)
    {
	// Add to node_clist
	stimulus = gui_node->node->stimuli;

	while(stimulus!=0)
	{
	    int row;

	    strncpy(name, stimulus->name().c_str(), sizeof(name));

	    row = gtk_clist_append(GTK_CLIST(gui_node->bbw->node_clist),
				   text);

	    gtk_clist_set_row_data (GTK_CLIST(gui_node->bbw->node_clist),
				    row,
				    stimulus);

	    stimulus = stimulus->next;
	}
    }

    gui_node->bbw->selected_node = gui_node;
}

static void settings_clist_cb(GtkCList       *clist,
		gint            row,
		gint            column,
		GdkEvent       *event,
		Breadboard_Window *bbw)
{

	// Save the Attribute*
	Value *attr;
	char str[256];
	char val[256];
	attr = (Value*) gtk_clist_get_row_data(GTK_CLIST(bbw->attribute_clist),
					       row);

	//attr->getAsStr(attrstr,50);
	attr->get(val, sizeof(val));

	sprintf(str,"%s = %s",attr->name().c_str(),val);
	
	gtk_entry_set_text(GTK_ENTRY(bbw->attribute_entry), str);
}

static void settings_set_cb(GtkWidget *button, 
		Breadboard_Window *bbw)
{

	const char *entry_string;
	char attribute_name[256];
	char attribute_newval[256];
	// We get here from both the button and entry->enter
	
	// Check the entry.
	entry_string=gtk_entry_get_text(GTK_ENTRY(bbw->attribute_entry));
	sscanf(entry_string,"%s = %s",attribute_name, attribute_newval);

	printf("change attribute \"%s\" to \"%s\"\n",attribute_name, attribute_newval);
	
	Value *attr;
	
	// Change the Attribute
	//attr = bbw->selected_module->module->get_attribute(attribute_name);
	attr = get_symbol_table().find(attribute_name);
	if(attr)
	{
	  try {

	    // Set attribute
	    attr->set(attribute_newval);

	    // Update clist
	    treeselect_module(0, bbw->selected_module);
	  }
	  catch (Error *err) {

	    if(err)
	      cout << __FUNCTION__ <<": " << err->toString() << endl;
	    delete err;
	  }

	}
	else
	{
	  printf("Could not find attribute \"%s\"\n",attribute_name);
	}
}

static void UpdateModuleFrame(GuiModule *p, Breadboard_Window *bbw)
{
  char buffer[STRING_SIZE];

  snprintf(buffer,sizeof(buffer),"%s settings",p->module()->name().c_str());
  gtk_frame_set_label(GTK_FRAME(p->bbw()->module_frame),buffer);

  if( !GTK_WIDGET_VISIBLE(GTK_CLIST(p->bbw()->attribute_clist)))
    return;

  // clear clist
  gtk_clist_clear(GTK_CLIST(p->bbw()->attribute_clist));

  // read attributes and add to clist
  char attribute_string[STRING_SIZE];
  char *text[1]={attribute_string};

  list <Value *>::iterator attribute_iterator;
  int row;

  for (attribute_iterator = p->module()->attributes.begin();
       attribute_iterator != p->module()->attributes.end();
       attribute_iterator++) {

    try {

      char attribute_value[STRING_SIZE];
      Value *locattr = *attribute_iterator;
      locattr->get(attribute_value, sizeof(attribute_value));

      sprintf(attribute_string,"%s = %s",locattr->name().c_str(),attribute_value);

      row = gtk_clist_append(GTK_CLIST(p->bbw()->attribute_clist),
			   text);
      // add the Attribute* as data for the clist rows.
      gtk_clist_set_row_data(GTK_CLIST(p->bbw()->attribute_clist),
			     row,
			     (gpointer)locattr);
    }

    catch (Error *err) {

      if(err)
	cout << "UpdateModuleFrame:" << err->toString() << endl;
      delete err;
    }

				    
  }

  gtk_entry_set_text(GTK_ENTRY(p->bbw()->attribute_entry), "");

}

static void treeselect_module(GtkItem *item, GuiModule *p)
{
  if (p) {

    gtk_widget_hide(p->bbw()->stimulus_frame);
    gtk_widget_hide(p->bbw()->node_frame);
    gtk_widget_hide(p->bbw()->pic_frame);

    gtk_widget_show(p->bbw()->module_frame);

    UpdateModuleFrame(p, p->bbw());
  

    p->bbw()->selected_module = p;
  }
}

void GuiModule::SetPosition(int nx, int ny)
{
  GList *piniter;

  nx=nx-nx%pinspacing;
  ny=ny-ny%pinspacing;

  if(nx != m_x || ny != m_y) {
    m_x=nx;
    m_y=ny;

    // Position module_widget
    if (m_pinLabel_widget)
      gtk_layout_move(GTK_LAYOUT(m_bbw->layout), m_pinLabel_widget,
		      m_x, 
		      m_y);
    if (m_module_widget) 
      gtk_layout_move(GTK_LAYOUT(m_bbw->layout), m_module_widget,
		      m_x + m_module_x, 
		      m_y + m_module_y);

    // Position module_name
    gtk_layout_move(GTK_LAYOUT(m_bbw->layout), m_name_widget, m_x, m_y-20);

    // Position pins
    piniter = m_pins;
    while(piniter!=0) {

      GuiPin *pin = static_cast<GuiPin *>(piniter->data);

      if(pin->orientation==RIGHT)
	pin->SetPosition(m_x + pin->module_x()+PINLENGTH,m_y + pin->module_y() + pin->height()/2);
      else
	pin->SetPosition(m_x + pin->module_x(), m_y + pin->module_y() + pin->height()/2);

      gtk_layout_move(GTK_LAYOUT(m_bbw->layout),
		      pin->m_pinDrawingArea,m_x+pin->module_x(),m_y+pin->module_y());

      piniter = piniter->next;
    }
  }
}

/* FIXME: calculate distance to the edges instead of the corners. */
double GuiModule::Distance(int px, int py)
{
  double distance;
  double min_distance=100000000;

  // Upper left
  distance=sqrt((double)abs(m_x-px)*abs(m_x-px) +
		abs(m_y-py)*abs(m_y-py));
  if(distance<min_distance)
    min_distance=distance;

  // Upper right
  distance=sqrt((double)abs(m_x+m_width-px)*abs(m_x + m_width - px) +
		abs(m_y-py)*abs(m_y-py));
  if(distance<min_distance)
    min_distance=distance;

  // Lower left
  distance=sqrt((double)abs(m_x-px)*abs(m_x-px) +
		abs(m_y + m_height-py)*abs(m_y+m_height-py));
  if(distance<min_distance)
    min_distance=distance;

  // Lower right
  distance=sqrt((double)abs(m_x+m_width-px)*abs(m_x+m_width-px) +
		abs(m_y+m_height-py)*abs(m_y+m_height-py));
  if(distance<min_distance)
    min_distance=distance;

  return min_distance;
}

static GuiModule *find_closest_module(Breadboard_Window *bbw, int x, int y)
{
  GuiModule *closest=0;
  double distance, min_distance=1000000;

  GList *mi = bbw->modules;

  while(mi) {

    GuiModule *p = static_cast<GuiModule *>(mi->data);

    distance = p->Distance(x,y);
    if(distance<min_distance) {
      closest = p;
      min_distance = distance;
    }

    mi=mi->next;
  }

  return closest;
}

// FIXME
static GuiModule *dragged_module=0;
static int dragging=0;
static int grab_next_module=0;

void grab_module(GuiModule *p)
{
    dragged_module = p;
    gdk_pointer_grab(p->bbw()->layout->window,
		     TRUE,
		     (GdkEventMask)(GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK),
		     p->bbw()->layout->window,
		     0,
                     GDK_CURRENT_TIME);

    treeselect_module(0,dragged_module);
    dragging = 1;
    clear_nodes(p->bbw());
    draw_nodes(p->bbw());
    gtk_widget_set_app_paintable(p->bbw()->layout, FALSE);
}

static void pointer_cb(GtkWidget *w,
		       GdkEventButton *event,
		       Breadboard_Window *bbw)
{
    static int x,y;

    x = (int) (event->x 
#if GTK_MAJOR_VERSION < 2
    	+ bbw->hadj->value
#endif
	);
    y = (int) (event->y 
#if GTK_MAJOR_VERSION < 2
    	+ bbw->vadj->value
#endif
	);

    switch(event->type)
    {
    case GDK_MOTION_NOTIFY:
	if(dragging && 0 != dragged_module)
	{
            dragged_module->SetPosition(x+pinspacing, y+pinspacing);
	    Value *xpos = dragged_module->module()->get_attribute("xpos", false);
	    Value *ypos = dragged_module->module()->get_attribute("ypos", false);
	    if(xpos)
	      xpos->set(dragged_module->x());
	    if(ypos)
	      ypos->set(dragged_module->y());
	}
	break;
    case GDK_BUTTON_PRESS:
	if(grab_next_module)
	{
	    if(dragging)
	    {
		gdk_pointer_ungrab(GDK_CURRENT_TIME);
		dragging = 0;
		gtk_widget_set_app_paintable(bbw->layout, TRUE);
		grab_next_module=0;
		update_board_matrix(bbw);
	    }
	}
	else
	{
	    dragged_module = find_closest_module(bbw, x, y);
            if (0 != dragged_module)
            {
	      gdk_pointer_grab(w->window,
			      TRUE,
			      (GdkEventMask)(GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK),
			      w->window,
			      0,
			      GDK_CURRENT_TIME);
	      treeselect_module(0,dragged_module);
	      dragging = 1;
	      clear_nodes(bbw);
              draw_nodes(bbw);
	      gtk_widget_set_app_paintable(bbw->layout, FALSE);
            }
	}
	break;
    case GDK_2BUTTON_PRESS:
	break;
    case GDK_BUTTON_RELEASE:
	if(dragging)
	{
	    gdk_pointer_ungrab(GDK_CURRENT_TIME);
	    update_board_matrix(bbw);
	    dragging = 0;
	    gtk_widget_set_app_paintable(bbw->layout, TRUE);
	    update_board_matrix(bbw);
	    UpdateModuleFrame(dragged_module, bbw);
	}
	break;
    default:
	printf("Whoops? event type %d\n",event->type);
	break;
    }
}

// When clicked on a pin
static gint button(GtkWidget *widget,
		   GdkEventButton *event,
		   GuiPin *p)
{
    if(event->type==GDK_BUTTON_PRESS &&
       event->button==1)
    {
      if(p->getSnode()) {
	struct gui_node *gn;

	gn = (struct gui_node *)
	  gtk_object_get_data(GTK_OBJECT(p->bbw()->node_tree),
			      p->getSnode()->name().c_str());

	if(gn!=0) {
	  treeselect_node(0, gn);
	  return 1;
	}
      }

      treeselect_stimulus(0, p);
      //puts("Stimulus should now be selected");

      return 1;
    }

    if(event->type==GDK_2BUTTON_PRESS &&
       event->button==1)
    {
      p->toggleState();
      return 1;
    }

    if(event->type==GDK_BUTTON_PRESS &&
       event->button==2)
    {
      if(p->getSnode()) {

	struct gui_node *gn;

	gn = (struct gui_node *)
	  gtk_object_get_data(GTK_OBJECT(p->bbw()->node_tree),
			      p->getSnode()->name().c_str());

	trace_node(gn);
	draw_nodes(gn->bbw);
      }
      return 1;
    }

    return 0;
}



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

    const char *string;
    
    retval=-1;

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

	label=gtk_label_new("Enter string:");
	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);
	GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
	gtk_signal_connect(GTK_OBJECT(entry),
			   "activate",
			   (GtkSignalFunc)a_cb,
			   (gpointer)&retval);

    }
    else
    {
	gtk_label_set_text(GTK_LABEL(label),prompt);
    }

    gtk_entry_set_text(GTK_ENTRY(entry), initial_text);

    gtk_widget_show(dialog);

    gtk_widget_grab_focus (entry);

    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)
	string=gtk_entry_get_text(GTK_ENTRY(entry));
    else
        string=0;
    
    return string;
}

static void add_new_snode(GtkWidget *button, Breadboard_Window *bbw)
{
    const char *node_name = gui_get_string("Node name","");

    if(node_name !=0)
	new Stimulus_Node(node_name);
}



////////////////////////////////////////////////////////////////////
static gint ok_cb(GtkWidget *widget,
		   GdkEventButton *event,
		    gpointer user_data)
{
    if(event->type==GDK_2BUTTON_PRESS &&
       event->button==1)
    {
	*(int*)user_data=FALSE; // cancel=FALSE;
	return 1;
    }
    return 0;
}
static void cancel_cb(GtkWidget *w, gpointer user_data)
{
    *(int*)user_data=TRUE; // cancel=TRUE;
}

// Select row
static void node_cb(GtkCList       *clist,
		    gint            row,
		    gint            column,
		    GdkEvent       *event,
		    gpointer user_data)

{
    Stimulus_Node *snode;

    snode = (Stimulus_Node*)gtk_clist_get_row_data (clist, row);

    *((Stimulus_Node**) user_data)=snode;
}
static void module_cb(GtkCList       *clist,
		      gint            row,
		      gint            column,
		      GdkEvent       *event,
		      gpointer user_data)
{
    char *module_type;

    module_type = (char*) gtk_clist_get_row_data (clist, row);

    strncpy((char*) user_data, module_type, STRING_SIZE);
}
static void copy_node_tree_to_clist(GtkWidget *item, gpointer clist)
{
    Stimulus_Node *node;
    char name[STRING_SIZE];
    char *text[1]={name};
    int row;

    node = (Stimulus_Node*)gtk_object_get_data(GTK_OBJECT(item), "snode");


    strcpy(name,node->name().c_str());

    row = gtk_clist_append(GTK_CLIST(clist),
			   text);

    gtk_clist_set_row_data (GTK_CLIST(clist),
			    row,
			    (gpointer)node);
}

static Stimulus_Node *select_node_dialog(Breadboard_Window *bbw)
{
    static GtkWidget *dialog;
    static GtkWidget *node_clist;
    GtkWidget *cancelbutton;
    static int cancel;

    cancel=-1;

    Stimulus_Node *snode=0;

    GtkWidget *vbox;
    GtkWidget *scrolledwindow;

    if(dialog==0)
    {

        // Build window
	dialog = gtk_dialog_new();
	gtk_window_set_title (GTK_WINDOW (dialog), "Select node to connect to");

	vbox = GTK_DIALOG(dialog)->vbox;

	scrolledwindow = gtk_scrolled_window_new (0, 0);
	gtk_widget_show (scrolledwindow);
	gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

	node_clist = gtk_clist_new (1);
	gtk_widget_show (node_clist);
	gtk_container_add (GTK_CONTAINER(scrolledwindow), node_clist);

	cancelbutton = gtk_button_new_with_label ("Cancel");
	gtk_widget_show (cancelbutton);
	gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->action_area), cancelbutton, FALSE, FALSE, 0);
	gtk_signal_connect(GTK_OBJECT(cancelbutton),"clicked",
			   GTK_SIGNAL_FUNC(cancel_cb),(gpointer)&cancel);

	gtk_signal_connect(GTK_OBJECT(node_clist),
			   "select_row",
			   (GtkSignalFunc) node_cb,
			   (gpointer)&snode);
	gtk_signal_connect(GTK_OBJECT(node_clist),
			   "button_press_event",
			   GTK_SIGNAL_FUNC(ok_cb),
			   (gpointer)&cancel);

	gtk_window_set_default_size(GTK_WINDOW(dialog), 220, 400);
    }
    gtk_clist_clear(GTK_CLIST(node_clist));

    // Add all nodes
    gtk_container_foreach(GTK_CONTAINER(bbw->node_tree),
			  copy_node_tree_to_clist,
			  (gpointer)node_clist);

    gtk_widget_show(dialog);

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


    if(cancel==(int)TRUE)
    {
	gtk_widget_hide(dialog);
	return 0;
    }

    gtk_widget_hide(dialog);

    return snode;
}

static char *select_module_dialog(Breadboard_Window *bbw)
{
    static GtkWidget *dialog;
    static GtkWidget *module_clist;
    GtkWidget *cancelbutton;
    static int cancel;
    static char module_type[STRING_SIZE];

    GtkWidget *vbox;
    GtkWidget *scrolledwindow;

    char *module_clist_titles[]={"Name","Library"};

    cancel=-1;
	
    if(dialog==0)
    {

        // Build window
	dialog = gtk_dialog_new();
	gtk_window_set_title (GTK_WINDOW (dialog), "Select module to load");

	vbox = GTK_DIALOG(dialog)->vbox;

	scrolledwindow = gtk_scrolled_window_new (0, 0);
	gtk_widget_show (scrolledwindow);
	gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

	module_clist = gtk_clist_new_with_titles (2, module_clist_titles);
	gtk_clist_set_column_auto_resize(GTK_CLIST(module_clist),0,TRUE);
	gtk_widget_show (module_clist);
	gtk_container_add (GTK_CONTAINER(scrolledwindow), module_clist);

	cancelbutton = gtk_button_new_with_label ("Cancel");
	gtk_widget_show (cancelbutton);
	gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->action_area), cancelbutton, FALSE, FALSE, 0);
	gtk_signal_connect(GTK_OBJECT(cancelbutton),"clicked",
			   GTK_SIGNAL_FUNC(cancel_cb),(gpointer)&cancel);

	gtk_signal_connect(GTK_OBJECT(module_clist),
			   "select_row",
			   (GtkSignalFunc) module_cb,
			   (gpointer)&module_type);
	gtk_signal_connect(GTK_OBJECT(module_clist),
			   "button_press_event",
			   GTK_SIGNAL_FUNC(ok_cb),
			   (gpointer)&cancel);

	gtk_window_set_default_size(GTK_WINDOW(dialog), 220, 400);
    }

    gtk_clist_clear(GTK_CLIST(module_clist));

  ModuleLibrary::FileList::iterator  mi;
  ModuleLibrary::FileList::iterator  itFileListEnd(ModuleLibrary::GetFileList().end());
        // Add all modules
  for (mi = ModuleLibrary::GetFileList().begin();
	     mi != itFileListEnd;
	     mi++)
	{

    ModuleLibrary::File *t = *mi;
	  cout << t->name() << '\n';
    Module_Types * pFileTypes;
	  if((pFileTypes = t->get_mod_list())) {
      // Loop through the list and display all of the modules.
      int i=0;
    
      while(pFileTypes[i].names[0])
      {   
        char name[STRING_SIZE];
        char library[STRING_SIZE];
        char *text[2]={name, library};
        int row;

        strncpy(name,pFileTypes[i].names[0], STRING_SIZE);
        strncpy(library,t->name(), STRING_SIZE);

        row = gtk_clist_append(GTK_CLIST(module_clist),
                               text);

        gtk_clist_set_row_data (GTK_CLIST(module_clist),
                                row,
                                (gpointer)pFileTypes[i].names[0]);

        i++;
      }
    }
  }

  gtk_widget_show(dialog);

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


  if(cancel==(int)TRUE)
  {
    gtk_widget_hide(dialog);
    return 0;
  }

  gtk_widget_hide(dialog);

  return module_type;
}

#if 0
// Display a file in a text widget.
static void text_dialog(const char *filename)
{
    static GtkWidget *dialog;
    GtkWidget *cancelbutton;

	GtkWidget *vbox;
	GtkWidget *scrolledwindow;
	GtkWidget *text;

        char string[STRING_SIZE];

	FILE *fi=fopen(filename,"r");
	if(fi==0)
            return;

	if(dialog!=0)
	    gtk_widget_destroy(dialog);


        // Build window
	dialog = gtk_dialog_new();
	gtk_window_set_title (GTK_WINDOW (dialog), filename);

	vbox = GTK_DIALOG(dialog)->vbox;

	scrolledwindow = gtk_scrolled_window_new (0, 0);
	gtk_widget_show (scrolledwindow);
	gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

	text = gtk_text_new(0,0);
	gtk_container_add (GTK_CONTAINER(scrolledwindow), text);
        gtk_widget_show(text);

	cancelbutton = gtk_button_new_with_label ("Ok");
	gtk_widget_show (cancelbutton);
	gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->action_area), cancelbutton, FALSE, FALSE, 0);
	gtk_signal_connect_object(GTK_OBJECT(cancelbutton),"clicked",
				  GTK_SIGNAL_FUNC(gtk_widget_hide),GTK_OBJECT(dialog));

	gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 400);


	while(fgets(string, sizeof(string), fi)!=0)
	{
	    gtk_text_insert(GTK_TEXT(text),
			    0,
			    0,
			    0,
			    string,
			    strlen(string));
	}

	fclose(fi);

    gtk_widget_show(dialog);

    return;
}
#endif

static void stimulus_add_node(GtkWidget *button, Breadboard_Window *bbw)
{

    Stimulus_Node *node;


    node = select_node_dialog(bbw);

    if(node!=0 && bbw->selected_pin!=0)
    {
	node->attach_stimulus(bbw->selected_pin->getIOpin());

        // Update stimulus frame
	treeselect_stimulus(0, bbw->selected_pin);
    }
}

static void add_library(GtkWidget *button, Breadboard_Window *bbw)
{

    const char *library_name;

    library_name = gui_get_string("Module library name (e.g. libgpsim_modules)","");

    if(library_name)
      ModuleLibrary::LoadFile(library_name);
}

static void add_module(GtkWidget *button, Breadboard_Window *bbw)
{

    char *module_type;
    const char *module_name;

    module_type = select_module_dialog(bbw);

    if(module_type!=0)
    {
        module_name = gui_get_string("Module name", module_type);
        grab_next_module = 1;
        if(module_name != 0)
          ModuleLibrary::NewObject(module_type, module_name);
    }
}

static void remove_module(GtkWidget *button, Breadboard_Window *bbw)
{
    GList *pin_iter;

    delete(bbw->selected_module->module());

    // FIXME the rest should be as callback from src


    // Remove pins
    pin_iter=bbw->selected_module->pins();
    while(pin_iter!=0)
    {
	GuiPin *pin = static_cast<GuiPin*>(pin_iter->data);

	gtk_widget_destroy(GTK_WIDGET(pin->m_pinDrawingArea));

	pin_iter = pin_iter->next;
    }

    // Remove widget
    gtk_container_remove(GTK_CONTAINER(bbw->layout),
			 bbw->selected_module->module_widget());
    gtk_container_remove(GTK_CONTAINER(bbw->layout),
			 bbw->selected_module->name_widget());

    // Remove from local list of modules
    bbw->modules=g_list_remove(bbw->modules, bbw->selected_module);

    // Remove module from tree
    gtk_container_remove(GTK_CONTAINER(bbw->tree),
			 bbw->selected_module->tree_item());

    gtk_widget_hide(bbw->module_frame);
    gtk_widget_hide(bbw->pic_frame);

    free(bbw->selected_module);

    bbw->selected_module=0;
}

static void node_clist_cb(GtkCList       *clist,
			  gint            row,
			  gint            column,
			  GdkEvent       *event,
			  Breadboard_Window *bbw)

{
    bbw->selected_node->selected_row = row;
}

static void remove_node(GtkWidget *button, Breadboard_Window *bbw)
{
    gtk_object_remove_data(GTK_OBJECT(bbw->node_tree),
			   bbw->selected_node->node->name().c_str());

    gtk_object_remove_data(GTK_OBJECT(bbw->selected_node->tree_item), "snode");

    gtk_container_remove(GTK_CONTAINER(bbw->node_tree),
			 bbw->selected_node->tree_item);

    delete bbw->selected_node->node;

    free(bbw->selected_node);

    bbw->selected_node=0;

    gtk_widget_hide(bbw->node_frame);
    gtk_widget_hide(bbw->stimulus_frame);
    gtk_widget_hide(bbw->module_frame);
    gtk_widget_hide(bbw->pic_frame);
}

static void remove_node_stimulus(GtkWidget *button, Breadboard_Window *bbw)
{
    stimulus *s;

    s = (stimulus*) gtk_clist_get_row_data(GTK_CLIST(bbw->node_clist), bbw->selected_node->selected_row);

    bbw->selected_node->node->detach_stimulus(s);

    gtk_clist_remove(GTK_CLIST(bbw->node_clist), bbw->selected_node->selected_row);

    bbw->selected_node->selected_row=-1;
}

//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
static const char *file_selection_name;
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 const char *gui_get_filename(char *filename)
{
    static GtkWidget *window = 0;

    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 (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);
    }

    gtk_file_selection_set_filename(GTK_FILE_SELECTION (window),
				    filename);

    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)
    {
	return 0;
    }

    return file_selection_name;
}
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
static void save_stc(GtkWidget *button, Breadboard_Window *bbw)
{
    FILE *fo;
    GList *module_iterator;
    Module *m;
    const char *filename;

    filename = gui_get_filename("netlist.stc");
    if(filename == 0)
        filename="/tmp/foo.stc";
    fo = fopen(filename, "w");

    fprintf(fo, "\n# This file was written by gpsim.\n");
    fprintf(fo, "\n# You can use this file for example like this:");
    fprintf(fo, "\n#     gpsim -s mycode.cod -c netlist.stc\n");
    fprintf(fo, "\n# If you want to add commands, you can create another .stc file");
    fprintf(fo, "\n# and load this file from it. Something like this:");
    fprintf(fo, "\n# ----------- myproject.stc ---------------");
    fprintf(fo, "\n# load s mycode.cod");
    fprintf(fo, "\n# frequency 12000000");
    fprintf(fo, "\n# load c netlist.stc");
    fprintf(fo, "\n# -----------------------------------------");
    fprintf(fo, "\n# You can then just load this new file:");
    fprintf(fo, "\n#     gpsim -c myproject.stc");
    fprintf(fo, "\n# and use netlist.stc whenever you save from the breadboard.");
    fprintf(fo, "\n#");
    fprintf(fo, "\n");

    fprintf(fo, "\n\n# Processor position:\n");

    /*m=bbw->gp->cpu;
    Attribute *xpos = m->get_attribute("xpos", false);
    Attribute *ypos = m->get_attribute("ypos", false);
    if(xpos && ypos && xpos->nGet()>=0 && ypos->nGet()>=0)
      fprintf(fo, "module position %s %d %d\n",
		m->name(),
		xpos->nGet(),
		ypos->nGet());*/
    
    // Save module libraries
    fprintf(fo, "\n\n# Module libraries:\n");
    ModuleLibrary::FileList::iterator  mi;
          // Add all modules
    for (mi = ModuleLibrary::GetFileList().begin();
	      mi != ModuleLibrary::GetFileList().end();
	      mi++)
    {

      ModuleLibrary::File *t = *mi;
      fprintf(fo, "module library %s\n",
      t->name());
    }

    // Save modules
    fprintf(fo, "\n\n# Modules:\n");
    module_iterator = bbw->modules;
    while(module_iterator!=0)
    {
      GuiModule *p;

      p = static_cast<GuiModule*>( module_iterator->data);

      list <Value *> :: iterator attribute_iterator;
      m = p->module();

      Processor *cpu;
      cpu=dynamic_cast<Processor*>(m);
      if(cpu==0)
      { // Module, not a processor, so add the load command
        fprintf(fo, "module load %s %s\n",
        m->type(),
        m->name().c_str());
      }

      for(attribute_iterator = m->attributes.begin();
          attribute_iterator != m->attributes.end();
          attribute_iterator++) {

        Value *locattr = *attribute_iterator;

        fprintf(fo, "%s=%s\n",
                locattr->name().c_str(),
                locattr->toString().c_str());
      }
      fprintf(fo, "\n");
      module_iterator=module_iterator->next;
    }

    // Save nodes and connections
    fprintf(fo, "\n\n# Connections:\n");
    list <Stimulus_Node *> :: iterator node_iterator;

    Symbol_Table &ST = get_symbol_table();
    Symbol_Table::node_symbol_iterator it;
    Symbol_Table::node_symbol_iterator itEnd = ST.endNodeSymbol();
    for(it = ST.beginNodeSymbol(); it != itEnd; it++) {
      Stimulus_Node *node = (*it)->getNode();
      assert(node != NULL);
      stimulus *stimulus;

      fprintf(fo, "node %s\n",node->name().c_str());

      if(node->stimuli!=0)
      {
        fprintf(fo, "attach %s",node->name().c_str());

        stimulus = node->stimuli;

        while(stimulus!=0)
        {
          fprintf(fo, " %s",stimulus->name().c_str());

          stimulus = stimulus->next;
        }

        fprintf(fo, "\n\n");
      }
    }

    fprintf(fo, "\n\n# End.\n");
    fclose(fo);
    //text_dialog(filename);

}

static void clear_traces(GtkWidget *button, Breadboard_Window *bbw)
{

    update_board_matrix(bbw);
}

static void trace_all_foreach_function(GtkWidget *item, gpointer bbw_gpointer)
{
    Stimulus_Node *node;
    Breadboard_Window *bbw = (Breadboard_Window*)bbw_gpointer;

    node = (Stimulus_Node*)gtk_object_get_data(GTK_OBJECT(item), "snode");

    struct gui_node * gn = (struct gui_node*) gtk_object_get_data(GTK_OBJECT(((Breadboard_Window*)bbw)->node_tree), node->name().c_str());

    trace_node(gn);
}

static void trace_all(GtkWidget *button, Breadboard_Window *bbw)
{

    update_board_matrix(bbw);

    gtk_container_foreach(GTK_CONTAINER(bbw->node_tree),
			  trace_all_foreach_function,
			  (gpointer)bbw);

    draw_nodes(bbw);

    if (verbose)
        puts("Trace all is done.");
}

//========================================================================
GuiBreadBoardObject::GuiBreadBoardObject(Breadboard_Window *bbw,int x, int y)
  : m_bbw(bbw), m_x(x), m_y(y), m_width(0), m_height(0), m_bIsBuilt(false)
{
}
GuiBreadBoardObject::~GuiBreadBoardObject()
{
}

void GuiBreadBoardObject::SetPosition(int x, int y)
{
  m_x = x;
  m_y = y;
}

void GuiPin::SetModulePosition(int x, int y)
{
  m_module_x = x;
  m_module_y = y;
}

//========================================================================
GuiPin::GuiPin(Breadboard_Window *_bbw,
	       GuiModule *pModule,
	       IOPIN *_iopin,
	       unsigned int pin_number)
  : GuiBreadBoardObject(_bbw, 0, 0), 
    m_pModule(pModule), m_module_x(0), m_module_y(0),
    m_label_x(0), m_label_y(0),
    m_pkgPinNumber(pin_number)
{

  iopin = _iopin;
  m_width=pinspacing;
  m_height=pinspacing;

  orientation = LEFT;

  gc=m_bbw->pinline_gc;

  if(iopin) {
    value=iopin->getState();
    direction=iopin->get_direction()==0?PIN_INPUT:PIN_OUTPUT;
    //orientation=_orientation;
    type=PIN_DIGITAL;
  }
  else
    {
      value=false;
      direction=PIN_INPUT;
      //orientation=_orientation;
      type=PIN_OTHER;
    }

  // Create widget
  m_pinDrawingArea = gtk_drawing_area_new();
  gtk_widget_set_events(m_pinDrawingArea,
			gtk_widget_get_events(m_pinDrawingArea)|
			GDK_BUTTON_PRESS_MASK);
  gtk_signal_connect(GTK_OBJECT(m_pinDrawingArea),
		     "button_press_event",
		     (GtkSignalFunc) button,
		     this);

  gtk_drawing_area_size(GTK_DRAWING_AREA(m_pinDrawingArea),m_width,m_height);
  gtk_signal_connect(GTK_OBJECT(m_pinDrawingArea),
		     "expose_event",
		     (GtkSignalFunc) expose_pin,
		     this);


  // Create pixmap for holding the pin graphics.
  pixmap = gdk_pixmap_new(m_bbw->window->window,
			  m_width,
			  m_height,
			  -1);

  // Draw pin
  //  Draw();

  gtk_widget_show(m_pinDrawingArea);


}

const char *GuiPin::pinName() const
{

  return iopin ? iopin->name().c_str() : 0;
}
//------------------------------------------------------------------------
// GuiPin::update() - check the state of the iopin and make the gui match
// 
void GuiPin::Update()
{

  if(iopin) {
	
    bool value=iopin->getState();
    eDirection dir=iopin->get_direction()==0?PIN_INPUT:PIN_OUTPUT;

    if(value!=getState() || dir!=direction) {
	  
      putState(value);
      direction=dir;

      Draw();
    }
  }


}
//------------------------------------------------------------------------
void GuiPin::toggleState()
{
  if(iopin) {
    char cPinState = iopin->getForcedDrivenState();

    switch (cPinState) {
    case '0':
    case 'Z':
    case 'X':
      iopin->forceDrivenState('1');
      break;
    case '1':
      iopin->forceDrivenState('0');
      break;
    case 'W':
      iopin->forceDrivenState('w');
      break;
    case 'w':
      iopin->forceDrivenState('W');
      break;
    }
    m_bbw->Update();
  }
}
//------------------------------------------------------------------------
// GuiPin::draw() - draw a single pin 
//
// 
void GuiPin::Draw()
{

  int pointx;
  int wingheight, wingx;
  int casex, endx;
  int y;

  switch(orientation)
    {
    case LEFT:
      casex = m_width;
      endx = 0;
      break;
    default:
      casex = 0;
      endx = m_width;
      break;
    }

  y = m_height/2;

  // Clear pixmap
  gdk_draw_rectangle (pixmap,
		      m_bbw->window->style->bg_gc[GTK_WIDGET_STATE (m_pinDrawingArea)],
		      TRUE,
		      0, 0,
		      m_width,
		      m_height);


  if(type==PIN_OTHER)
    gdk_gc_set_foreground(gc,&black_color);
  else
    gdk_gc_set_foreground(gc,getState() ? &high_output_color:&low_output_color);

  // Draw actual pin
  gdk_draw_line(pixmap,gc,
		casex,y,endx,y);

  if(type==PIN_OTHER)
    return;

  // Draw direction arrow
  wingheight=m_height/3;
    
  if(casex>endx)
    {
      if(direction==PIN_OUTPUT)
	{
	  pointx = endx + PINLENGTH/3;
	  wingx=endx+(PINLENGTH*2)/3;
	}
      else
	{
	  pointx = endx + (PINLENGTH*2)/3;
	  wingx=endx+PINLENGTH/3;
	}
    }
  else
    {
      if(direction==PIN_OUTPUT)
	{
	  pointx = casex + (PINLENGTH*2)/3;
	  wingx=casex+PINLENGTH/3;
	}
      else
	{
	  pointx = casex + PINLENGTH/3;
	  wingx=casex+(PINLENGTH*2)/3;
	}
    }

    
  // Draw an arrow poining at (endx,endy)
  gdk_draw_line(pixmap,gc,
		pointx,y,wingx,y+wingheight);
  gdk_draw_line(pixmap,gc,
		pointx,y,wingx,y-wingheight);

  if(m_pinDrawingArea->window!=0)
    gdk_draw_pixmap(m_pinDrawingArea->window,
		    m_pinDrawingArea->style->fg_gc[GTK_WIDGET_STATE (m_pinDrawingArea)],
		    pixmap,
		    0, 0,
		    0, 0,
		    m_width, m_height);
}
//------------------------------------------------------------------------

void GuiPin::SetLabelPosition(int x, int y)
{
  m_label_x = x;
  m_label_y = y;
}

//------------------------------------------------------------------------
// GuiPin::DrawLabel() - draw the label for a single pin 
//
// 
void GuiPin::DrawLabel(GdkPixmap *module_pixmap)
{
  const char *name;


  name = iopin ? iopin->name().c_str() : "";
  if(*name && m_bbw ) {

    gdk_draw_text(module_pixmap,
#if GTK_MAJOR_VERSION >= 2
		  gdk_font_from_description(m_bbw->pinnamefont),
#else
		  m_bbw->pinnamefont,
#endif
		  m_bbw->pinname_gc,
		  m_label_x,
		  m_label_y,
		  name,strlen(name));


  }
}
//------------------------------------------------------------------------
// GuiPin::DrawGUIlabel() - Erase label and change to that set by newGUIname
//
// 
int GuiPin::DrawGUIlabel(GdkPixmap *module_pixmap,  int pinnameWidths[])
{
  const char *name;
  int orient;


  name = iopin ? iopin->GUIname().c_str() : "";
  if(*name && m_bbw && iopin->is_newGUIname()) {
    iopin->clr_is_newGUIname();

    orient = (m_label_x <= LABELPAD+CASELINEWIDTH)?0:2; // Left or Right label?

    // Clear label area
    gdk_draw_rectangle (module_pixmap,
			m_bbw->window->style->white_gc,
                        TRUE,
		  	m_label_x,
		  	m_label_y-m_height+CASEOFFSET,
                        pinnameWidths[orient],
                        m_height);

    gdk_draw_text(module_pixmap,
#if GTK_MAJOR_VERSION >= 2
		  gdk_font_from_description(m_bbw->pinnamefont),
#else
		  m_bbw->pinnamefont,
#endif
		  m_bbw->pinname_gc,
		  m_label_x,
		  m_label_y,
		  name,strlen(name));


    return 1;
  }
  return 0;
}
//------------------------------------------------------------------------
void GuiPin::addXref(CrossReferenceToGUI *newXref)
{
  xref = newXref;
}
//------------------------------------------------------------------------
void GuiPin::Destroy()
{
  if(xref)
    iopin->remove_xref(xref);

  gdk_pixmap_unref(pixmap);
  gtk_widget_destroy(m_pinDrawingArea);
}

//------------------------------------------------------------------------
static gboolean name_expose(GtkWidget *widget, GdkEventExpose *event, GuiModule *p)
{
    if(p->name_pixmap()==0)
    {
	puts("bbw.c: no pixmap2!");
	return 0;
    }

    gdk_draw_pixmap(widget->window,
		    widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
		    p->name_pixmap(),
		    event->area.x, event->area.y,
		    event->area.x, event->area.y,
		    event->area.width, event->area.height);
    return 0;
}

static gboolean module_expose(GtkWidget *widget, GdkEventExpose *event, GuiModule *p)
{

    if(p->module_pixmap()==0)
    {
	puts("bbw.c: no pixmap3!");
	return 0;
    }

    gdk_draw_pixmap(widget->window,
		    widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
		    p->module_pixmap(),
		    event->area.x, event->area.y,
		    event->area.x, event->area.y,
		    event->area.width, event->area.height);
    return 0;
}

void GuiModule::Draw()
{
}

void GuiModule::Destroy()
{
}

#define PACKAGESPACING 15

void GuiModule::Update()
{

  GList *pin_iter;

  gtk_widget_ref(m_pinLabel_widget);
  gtk_container_remove(GTK_CONTAINER(m_bbw->layout),m_pinLabel_widget);

  // Delete the static module pixmap if there is no widget
  // in the module.
  if(m_module->get_widget()==0)
    {
      gdk_pixmap_unref(m_module_pixmap);
      gtk_widget_destroy(m_pinLabel_widget);
    }

  // Delete the pins
  pin_iter=m_pins;
  while(pin_iter!=NULL) {
    GuiPin *pin = static_cast<GuiPin*>(pin_iter->data);
    pin->Destroy();
    pin_iter=pin_iter->next;
  }
    
  // Destroy name widget
  gdk_pixmap_unref(m_name_pixmap);
  gtk_widget_destroy(m_name_widget);
    
  // Remove from gtk-tree 
  gtk_tree_item_remove_subtree(GTK_TREE_ITEM(m_tree_item));
  gtk_widget_destroy(m_tree_item);
    
  // Remove module from list
  m_bbw->modules=g_list_remove(m_bbw->modules, this);

  // rebuild module
  Build();

  gtk_widget_unref(m_pinLabel_widget);
}

void GuiModule::UpdatePins()
{
  int change = 0;
  GList *pin_iter;
  pin_iter=m_pins;
  while(pin_iter!=0) {
    GuiPin *pin = static_cast<GuiPin *>(pin_iter->data);
    change = pin->DrawGUIlabel(m_module_pixmap, pinnameWidths)?1:change;
    pin->Update();
    pin_iter = pin_iter->next;
  }
  if (change && m_pinLabel_widget->window)  // Pin label changed, Draw Labels
  {

      gdk_draw_pixmap(m_pinLabel_widget->window,
          m_pinLabel_widget->style->fg_gc[GTK_WIDGET_STATE (m_pinLabel_widget)],
          m_module_pixmap,
          0, 0,
          0, 0,
          m_width, m_height);
  }

}
//========================================================================
//========================================================================
class PositionAttribute : public Float
{
protected:
  Breadboard_Window *bbw;
public:
  PositionAttribute(Breadboard_Window *_bbw,const char *n, double v);
  void set(Value *v);
};

PositionAttribute::PositionAttribute(Breadboard_Window *_bbw, const char *n, double v)
  : Float(v), bbw(_bbw)
{
  new_name((char*)n);
}

void PositionAttribute::set(Value *v)
{
  Float::set(v);
  if(bbw)
    bbw->Update();
}

//------------------------------------------------------------------------
void GuiModule::DrawCaseOutline(GtkWidget *da)
{
#if GTK_CHECK_VERSION(2,8,7)

  gdk_draw_rectangle (m_module_pixmap,
		      m_bbw->window->style->bg_gc[GTK_WIDGET_STATE (m_bbw->window)],
		      TRUE,
		      0, 0,
		      m_width,
		      m_height);

  cairo_t *cr = gdk_cairo_create (m_module_pixmap);

  cairo_rectangle(cr, CASEOFFSET,CASEOFFSET, m_width-2*CASEOFFSET, m_height-2*CASEOFFSET);
  cairo_set_source_rgb (cr, 1, 1, 1);
  cairo_fill_preserve (cr);
  cairo_set_source_rgb (cr, 0, 0, 0);
  cairo_stroke (cr);


#else

  gdk_draw_rectangle (m_module_pixmap,
		      m_bbw->window->style->white_gc,
		      TRUE,
		      CASEOFFSET, CASEOFFSET,
		      m_width-CASEOFFSET,
		      m_height-CASEOFFSET);
    

  // Draw case outline
  gdk_gc_set_foreground(m_bbw->case_gc,&black_color);
  gdk_draw_line(m_module_pixmap,m_bbw->case_gc,CASEOFFSET,CASEOFFSET,
		m_width-CASEOFFSET,CASEOFFSET);
  gdk_draw_line(m_module_pixmap,m_bbw->case_gc,m_width-CASEOFFSET,CASEOFFSET,
		m_width-CASEOFFSET,m_height-CASEOFFSET);
  gdk_draw_line(m_module_pixmap,m_bbw->case_gc,CASEOFFSET,m_height-CASEOFFSET,
		m_width-CASEOFFSET,m_height-CASEOFFSET);
  gdk_draw_line(m_module_pixmap,m_bbw->case_gc,CASEOFFSET,CASEOFFSET,
		CASEOFFSET,m_height-CASEOFFSET);
#endif
}
//------------------------------------------------------------------------

// 
static float hackPackageHeight=0.0;

void GuiModule::AddPin(unsigned int pin_number)
{

  IOPIN *iopin = m_module->get_pin(pin_number);
  BreadBoardXREF *cross_reference = 0;
  if(iopin) {
    // Create xref
    cross_reference = new BreadBoardXREF();
    cross_reference->parent_window_type = WT_breadboard_window;
    cross_reference->parent_window = (gpointer) m_bbw;
    cross_reference->data = (gpointer) 0;
    iopin->add_xref(cross_reference);
  }

  GuiPin *pin = new GuiPin(m_bbw, this, iopin, pin_number);
  pin->addXref(cross_reference);
  m_pins = g_list_append(m_pins, pin);

}

void GuiModule::AddPinGeometry(GuiPin *pin)
{
  eOrientation orientation;
  unsigned int pin_number = pin->number();

  // Get the X and Y coordinates for this pin 
  // (the coordinates are referenced to the module's origin)
  int pin_x, pin_y;
  int label_x, label_y;

  const PinGeometry *pPinGeometry = m_module->package->getPinGeometry(pin_number);

  if (pPinGeometry->bNew) {
    switch (pPinGeometry->m_orientation) {

    case UP:
      pin_x   = (int)pPinGeometry->m_x;
      pin_y   = (int)pPinGeometry->m_y;

      label_x = pin_x + LABELPAD + CASELINEWIDTH;
      label_y = pin_y + LABELPAD + CASELINEWIDTH;

      orientation = UP;
      break;
    case LEFT:
      pin_x   = (int)pPinGeometry->m_x - pinspacing;
      pin_y   = (int)pPinGeometry->m_y;

      label_x = LABELPAD + CASELINEWIDTH;
      label_y = pin_y + LABELPAD + CASELINEWIDTH;

      orientation = LEFT;
      break;
    case RIGHT:
      pin_x = m_width + (int)pPinGeometry->m_x;
      pin_y = (int)pPinGeometry->m_y;

      label_x = pin_x + LABELPAD + CASELINEWIDTH + m_width/2;
      label_y = pin_y + LABELPAD + CASELINEWIDTH;

      orientation = RIGHT;
      break;
    case DOWN:
      pin_x = (int)pPinGeometry->m_x;
      pin_y = m_height + (int)pPinGeometry->m_y;

      label_x = pin_x + LABELPAD + CASELINEWIDTH;
      label_y = pin_y + LABELPAD + CASELINEWIDTH;

      orientation = DOWN;
      break;
    default:
      printf("################### Error:\n");
      printf("Undefined orientation.\n");
      assert(0);
    }
  } else {

    // old style -- to be deprecated.
    float pin_position=m_module->package->get_pin_position(pin_number);

    // Put pin in layout
    if(pin_position>=0.0 && pin_position<1.0) {
      pin_x=-pinspacing;
      pin_y=(int)(m_height/2+((pin_position-0.5)*hackPackageHeight))-pinspacing/2;
      orientation = LEFT;

      label_x=LABELPAD+CASELINEWIDTH;
      label_y=(int)(pin_position*hackPackageHeight);
      label_y+=LABELPAD+CASELINEWIDTH+pinspacing/2-m_bbw->pinnameheight/3;
      label_y+=PINLENGTH/2;

    }
    else if(pin_position>=2.0 && pin_position<3.0) {
      pin_x=m_width;
      pin_y=(int)(m_height/2+((3.0-pin_position-0.5)*hackPackageHeight))-pinspacing/2;
      orientation = RIGHT;

      label_x= m_width/2  + CASELINEWIDTH;
      label_y=(int)((3.0-pin_position)*hackPackageHeight);
      label_y+=LABELPAD+CASELINEWIDTH+pinspacing/2-m_bbw->pinnameheight/3;
      label_y+=PINLENGTH/2;

    }
    else {

      // FIXME

      printf("################### Error:\n");
      printf("Number of pins %d\n",m_module->package->number_of_pins);
      printf("pin_position %f\n",pin_position);
      printf("pin_position2 %f\n",m_module->package->get_pin_position(pin_number));
      printf("pin_number %d\n",pin_number);
      assert(0);
    }
  }

  pin->SetModulePosition(pin_x,pin_y);
  pin->SetLabelPosition(label_x,label_y);
  pin->SetOrientation(orientation);
  pin->Draw();

}

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
// experimental
//

#if 0//GTK_CHECK_VERSION(2,8,7)


static void CairoExperiment(Breadboard_Window *m_bbw,GdkPixmap *pixmap)
{

  static bool bThrough = false;
  if (bThrough)
    return;

  cairo_t *cr = gdk_cairo_create (pixmap);


  gdk_gc_set_foreground(m_bbw->case_gc,&black_color);
  //gdk_draw_line(pixmap,m_bbw->case_gc, 10,10,  40,40);


  double xc = 50;
  double yc = 50;
  double radius = 30;
  double angle1 = 45.0  * (M_PI/180.0);  /* angles are specified */
  double angle2 = 180.0 * (M_PI/180.0);  /* in radians           */

  /*
  cairo_arc (cr, xc, yc, radius, angle1, angle2);
  cairo_stroke (cr);

  cairo_set_source_rgba (cr, 1,0.2,0.2,0.6);
  cairo_arc (cr, xc, yc, 5, 0, 2*M_PI);
  cairo_fill (cr);

  cairo_set_line_width (cr, 1);
  cairo_arc (cr, xc, yc, radius, angle1, angle1);
  cairo_line_to (cr, xc, yc);
  cairo_arc (cr, xc, yc, radius, angle2, angle2);
  cairo_line_to (cr, xc, yc);
  */
  cairo_scale (cr, 100, 100);
  cairo_set_line_width (cr, 0.04);

  cairo_move_to (cr, 0.4, 0.1);
  cairo_line_to (cr, 0.1, 0.1);
  cairo_line_to (cr, 0.1, 0.8);
  cairo_line_to (cr, 0.8, 0.8);
  cairo_line_to (cr, 0.8, 0.1);
  cairo_line_to (cr, 0.55, 0.1);

  cairo_curve_to (cr, 0.45, 0.15, 0.55, 0.15, 0.45, 0.1);

  /*
  cairo_move_to (cr, 0.5, 0.1);
  cairo_line_to (cr, 0.9, 0.9);
  cairo_rel_line_to (cr, -0.4, 0.0);
  cairo_curve_to (cr, 0.2, 0.9, 0.2, 0.5, 0.5, 0.5);
  */

  cairo_close_path (cr);


  cairo_set_source_rgb (cr, 0, 0, 1);
  cairo_fill_preserve (cr);
  cairo_set_source_rgb (cr, 0, 0, 0);
  cairo_stroke (cr);
  //cairo_stroke (cr);

  bThrough=true;

}

#endif

// end experimental
//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

static void createLabel(GtkWidget **da,
			GdkPixmap **pixmap,
			GtkWidget *parent,
			GdkGC *gc,
			const char *text,
			PangoFontDescription *font)
{
  *da = gtk_drawing_area_new();

  int height = gStringHeight(font,text);
  int width  = gStringWidth(font,text);

  gtk_drawing_area_size(GTK_DRAWING_AREA(*da),width,height);
  *pixmap = gdk_pixmap_new(parent->window,
			   width,
			   height,
			   -1);
  gdk_draw_rectangle (*pixmap,
		      parent->style->bg_gc[GTK_WIDGET_STATE (*da)],
		      TRUE,
		      0, 0,
		      width,
		      height);
  gdk_draw_text(*pixmap,
		gFontFromDescription(font),
		gc,
		0,height,
		text,strlen(text));

}
void GuiModule::BuildReferenceDesignator()
{
  createLabel(&m_name_widget, &m_name_pixmap, m_bbw->window, m_bbw->pinname_gc,
	      m_module->name().c_str(),m_bbw->pinnamefont);
  gtk_signal_connect(GTK_OBJECT(m_name_widget),
		     "expose_event",
		     (GtkSignalFunc) name_expose,
		     this);


}

//------------------------------------------------------------------------
void GuiModule::Build()
{
  if(m_bIsBuilt || !m_bbw)
    return;

  if(!m_bbw->enabled)
    return;


  int i;
  int nx, ny;

  BreadBoardXREF *cross_reference;

  m_width=50;
  m_height=18;

  Package *package = m_module->package;
  if(!package)
    return;     // embedded module 


  m_module_widget = (GtkWidget *)m_module->get_widget();

  m_pins=0;
  m_pin_count=m_module->get_pin_count();

  Value *xpos = m_module->get_attribute("xpos", false);
  Value *ypos = m_module->get_attribute("ypos", false);
  xpos->get(nx);
  ypos->get(ny);

  m_tree_item = gtk_tree_item_new_with_label (m_module->name().c_str());
  gtk_signal_connect(GTK_OBJECT(m_tree_item),
		     "select",
		     (GtkSignalFunc) treeselect_module,
		     this);
  gtk_widget_show(m_tree_item);
  gtk_tree_append(GTK_TREE(m_bbw->tree), m_tree_item);

  hackPackageHeight=(float)((m_pin_count/2+(m_pin_count&1)-1)*pinspacing);

  // Find the length of the longest pin name in each direction
  // The directions are in the order of left, up , right, down.
  int pinmax_x = 0;
  int pinmax_y = 0;
  for(i=1;i<=m_pin_count;i++) {
    
    PinGeometry *pPinGeometry = m_module->package->getPinGeometry(i);

    pPinGeometry->convertToNew();
    if (pPinGeometry->bNew) {
      pinmax_x = pinmax_x < pPinGeometry->m_x ? pinmax_x : ((int) pPinGeometry->m_x);
      pinmax_y = pinmax_y < pPinGeometry->m_y ? pinmax_y : ((int) pPinGeometry->m_y);
    }

    int w=0;
    const char *name = m_module->get_pin_name(i).c_str();

    if(name && pPinGeometry->m_bShowPinname)
      w = gStringWidth(m_bbw->pinnamefont, name);

    if(w > pinnameWidths[pPinGeometry->m_orientation])
      pinnameWidths[pPinGeometry->m_orientation]= w;
    
    AddPin(i);
    
  }

  printf("Widths %d %d %d %d\n",
	 pinnameWidths[0],pinnameWidths[1],pinnameWidths[2],pinnameWidths[3]);

  bool bShowPinNames = true;

  if(!m_module_widget) {

    // Create a static representation.

    m_width =  pinnameWidths[0] + pinnameWidths[2] + 2 * FOORADIUS;
    m_width += 2*CASELINEWIDTH+2*LABELPAD;

    m_height = m_module->get_pin_count()/2*pinspacing; // pin name height

    if(m_module->get_pin_count()%2)
      m_height += pinspacing;

    m_height+=2*CASELINEWIDTH+2*LABELPAD;


    m_module_pixmap = gdk_pixmap_new(m_bbw->window->window,
				     m_width,
				     m_height,
				     -1);

    m_pinLabel_widget = gtk_drawing_area_new();

    gtk_drawing_area_size(GTK_DRAWING_AREA(m_pinLabel_widget),m_width,m_height);

    DrawCaseOutline(m_pinLabel_widget);

    gtk_signal_connect(GTK_OBJECT(m_pinLabel_widget),
		       "expose_event",
		       (GtkSignalFunc) module_expose,
		       this);

    gtk_widget_show(m_pinLabel_widget);


  } else {

    // Get the [from the module] provided widget's size
    GtkRequisition req;

    gtk_widget_size_request(m_module_widget, &req);

    bShowPinNames = false;

    m_width=req.width + (bShowPinNames ? (pinnameWidths[0] + pinnameWidths[2]) : 0 );
    m_height=req.height;
    m_module_x = bShowPinNames ? pinnameWidths[0] : 0;
    printf("module_x %d, module widget size %d, %d\n",m_module_x,m_width,m_height);

    m_module_pixmap = gdk_pixmap_new(m_bbw->window->window,
				     m_width,
				     m_height,
				     -1);
    gdk_draw_rectangle (m_module_pixmap,
			m_bbw->window->style->white_gc,
			TRUE,
			0,0,
			m_width,
			m_height);

    if (bShowPinNames) {
      m_pinLabel_widget = gtk_drawing_area_new();

      gtk_drawing_area_size(GTK_DRAWING_AREA(m_pinLabel_widget),m_width,m_height);

      DrawCaseOutline(m_pinLabel_widget);

      gtk_signal_connect(GTK_OBJECT(m_pinLabel_widget),
			 "expose_event",
			 (GtkSignalFunc) module_expose,
			 this);

      gtk_widget_show(m_pinLabel_widget);
    }

    gtk_widget_show(m_module_widget);
  }

  // Create xref
  cross_reference = new BreadBoardXREF();
  cross_reference->parent_window_type = WT_breadboard_window;
  cross_reference->parent_window = (gpointer) m_bbw;
  cross_reference->data = (gpointer) 0;
  m_module->xref->_add(cross_reference);



  // Create name_widget
  BuildReferenceDesignator();
  gtk_widget_show(m_name_widget);


  // Create pins
  GtkWidget *hackSubtree = gtk_tree_new();
  GtkWidget *sub_tree_item;

  gtk_widget_show(hackSubtree);
  gtk_tree_item_set_subtree(GTK_TREE_ITEM(m_tree_item), hackSubtree);

  GList *pin_iter;
  pin_iter=m_pins;
  while(pin_iter!=0) {
    GuiPin *pin = static_cast<GuiPin *>(pin_iter->data);
    AddPinGeometry(pin);
    pin->DrawLabel(m_module_pixmap);
    gtk_layout_put(GTK_LAYOUT(m_bbw->layout),pin->m_pinDrawingArea,0,0);

    // Add pin to tree
    const char *name=pin->pinName();
    if(name) {
      sub_tree_item = gtk_tree_item_new_with_label (name);

      gtk_signal_connect(GTK_OBJECT(sub_tree_item),
			 "select",
			 (GtkSignalFunc) treeselect_stimulus,
			 pin);
      gtk_widget_show(sub_tree_item);
      gtk_tree_append(GTK_TREE(hackSubtree), sub_tree_item);
    }

    pin_iter = pin_iter->next;
  }


  if (m_pinLabel_widget)
    gtk_layout_put(GTK_LAYOUT(m_bbw->layout), m_pinLabel_widget, 0, 0);
  if (m_module_widget)
    gtk_layout_put(GTK_LAYOUT(m_bbw->layout), m_module_widget, 0, 0);
  gtk_layout_put(GTK_LAYOUT(m_bbw->layout), m_name_widget, 0,0);

  SetPosition(nx, ny);
  xpos->set(m_x);
  ypos->set(m_y);

  m_bIsBuilt = true;

  update_board_matrix(m_bbw);

}

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

GuiModule::GuiModule(Module *_module, Breadboard_Window *_bbw)
  :  GuiBreadBoardObject(_bbw,0,0), m_module(_module)
{
  m_pinLabel_widget=0;
  m_module_widget=0;
  m_module_x = 0;
  m_module_y = 0;
  m_name_widget=0;
  m_width=0;
  m_height=0;
  m_pin_count=0;

  m_module_pixmap=0;
  m_name_pixmap=0;

  m_tree_item=0;

  m_pins=0;
  pinnameWidths[0] = 0;
  pinnameWidths[1] = 0;
  pinnameWidths[2] = 0;
  pinnameWidths[3] = 0;

  if(m_bbw)
    m_bbw->modules=g_list_append(m_bbw->modules, this);
}
//========================================================================
GuiDipModule::GuiDipModule(Module *_module, Breadboard_Window *_bbw)
  : GuiModule(_module, _bbw)
{
}

void GuiDipModule::DrawCaseOutline(GtkWidget *da)
{
#if GTK_CHECK_VERSION(2,8,7)

  gdk_draw_rectangle (m_module_pixmap,
		      m_bbw->window->style->bg_gc[GTK_WIDGET_STATE (m_bbw->window)],
		      TRUE,
		      0, 0,
		      m_width,
		      m_height);

  cairo_t *cr = gdk_cairo_create (m_module_pixmap);

  cairo_line_to (cr, CASEOFFSET, CASEOFFSET);
  cairo_line_to (cr, CASEOFFSET, m_height-2*CASEOFFSET);
  cairo_line_to (cr, m_width-CASEOFFSET, m_height-2*CASEOFFSET);
  cairo_line_to (cr, m_width-CASEOFFSET, CASEOFFSET);
  cairo_arc (cr, m_width/2-CASEOFFSET, CASEOFFSET, FOORADIUS, 0, M_PI);

  cairo_close_path (cr);

  cairo_set_source_rgb (cr, 1, 1, 1);
  cairo_fill_preserve (cr);
  cairo_set_source_rgb (cr, 0, 0, 0);
  cairo_stroke (cr);


#else

  gdk_draw_rectangle (m_module_pixmap,
		      m_bbw->window->style->white_gc,
		      TRUE,
		      CASEOFFSET, CASEOFFSET,
		      m_width-CASEOFFSET,
		      m_height-CASEOFFSET);
    


  // Draw case outline
  gdk_gc_set_foreground(m_bbw->case_gc,&black_color);
  gdk_draw_line(m_module_pixmap,m_bbw->case_gc,CASEOFFSET,CASEOFFSET,
		m_width/2-FOORADIUS,CASEOFFSET);
  gdk_draw_line(m_module_pixmap,m_bbw->case_gc,m_width-CASEOFFSET,CASEOFFSET,
		m_width/2+FOORADIUS,CASEOFFSET);
  gdk_draw_line(m_module_pixmap,m_bbw->case_gc,m_width-CASEOFFSET,CASEOFFSET,
		m_width-CASEOFFSET,m_height-CASEOFFSET);
  gdk_draw_line(m_module_pixmap,m_bbw->case_gc,CASEOFFSET,m_height-CASEOFFSET,
		m_width-CASEOFFSET,m_height-CASEOFFSET);
  gdk_draw_line(m_module_pixmap,m_bbw->case_gc,CASEOFFSET,CASEOFFSET,
		CASEOFFSET,m_height-CASEOFFSET);
  gdk_draw_arc(m_module_pixmap,m_bbw->window->style->bg_gc[GTK_WIDGET_STATE (da)],
	       TRUE,m_width/2-FOORADIUS,CASEOFFSET-FOORADIUS,2*FOORADIUS,2*FOORADIUS,
	       180*64,180*64);
  gdk_draw_arc(m_module_pixmap,m_bbw->case_gc,FALSE,m_width/2-FOORADIUS,CASEOFFSET-FOORADIUS,
	       2*FOORADIUS,2*FOORADIUS,180*64,180*64);

#endif
}

//========================================================================
void Breadboard_Window::Update(void)
{
  GList *iter;
  int x,y;

  // loop all modules and look for changes
  if(!enabled)
    return;

    
  if(!GTK_WIDGET_VISIBLE(window))
    return;

  iter=modules;
  while(iter!=0) {
    
    GuiModule *p = static_cast<GuiModule *>(iter->data);

    if(p->IsBuilt()) {

      // Check if module has changed number of pins
      if(p->pin_count()!=p->module()->get_pin_count())
	// If so, refresh the gui widget
	p->Update();

      // Check if module has changed its position
      Value *xpos = p->module()->get_attribute("xpos", false);
      Value *ypos = p->module()->get_attribute("ypos", false);
      if(xpos && ypos) {
	xpos->get(x);
	ypos->get(y);

	if(p->x()!=x || p->y()!=y) {
	  // If so, move the module
	  p->SetPosition(x, y);
	  update_board_matrix(p->bbw());
	}
      }

      // Check if pins have changed state
      p->UpdatePins();
    }
    iter = iter->next;
  }

}

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

/* When a processor is created */
void Breadboard_Window::NewProcessor(GUI_Processor *_gp)
{
  // Create a Gui representation (note that this memory is
  // placed onto the 'modules' list.
  
  Value *xpos = gp->cpu->get_attribute("xpos", false);
  Value *ypos = gp->cpu->get_attribute("ypos", false);
  if(!xpos || !ypos) {
    xpos = new PositionAttribute(this,"xpos",(double)80);
    ypos = new PositionAttribute(this,"ypos",(double)80);
    gp->cpu->add_attribute(xpos);
    gp->cpu->add_attribute(ypos);
  }

  if(!enabled)
    return;

  m_MainCpuModule = new GuiDipModule(gp->cpu, this);
  m_MainCpuModule->Build();

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

  Update();
}

/* When a module is created */
void Breadboard_Window::NewModule(Module *module)
{
  // If the xpos and ypos attributes does not exist, then create them.
  static int sx=80;
  static int sy=280;

  Value *xpos = module->get_attribute("xpos", false);
  Value *ypos = module->get_attribute("ypos", false);
  if(!xpos || !ypos) {
    xpos = new PositionAttribute(this,"xpos",(double)sx);
    ypos = new PositionAttribute(this,"ypos",(double)sy);
    module->add_attribute(xpos);
    module->add_attribute(ypos);
  }
  sy+=100;
  if(sy>LAYOUTSIZE_Y)
  {
  	sy=0;
	sx+=100;
	if(sx>LAYOUTSIZE_X)
		sx=50;
  }

  if(!enabled)
    return;

  GuiModule *p=new GuiModule(module, this);
  p->Build();

  if(grab_next_module)
    grab_module(p);

  Update();
}


/* When a stimulus is being connected or disconnected, or a new node is created */
void Breadboard_Window::NodeConfigurationChanged(Stimulus_Node *node)
{

  if(!enabled)
    return;

  struct gui_node * gn = (struct gui_node*) gtk_object_get_data(GTK_OBJECT(node_tree), node->name().c_str());

  if(gn==0) {
    GtkWidget *node_item;

    gn = (struct gui_node *) malloc(sizeof(*gn));

    gn->bbw=this;
    gn->node=node;

    node_item = gtk_tree_item_new_with_label (node->name().c_str());
    gn->tree_item = node_item;
    gtk_signal_connect(GTK_OBJECT(node_item),
		       "select",
		       (GtkSignalFunc) treeselect_node,
		       gn);
    gtk_widget_show(node_item);
    gtk_tree_append(GTK_TREE(node_tree), node_item);
    gtk_object_set_data(GTK_OBJECT(node_tree), node->name().c_str(), gn);
    gtk_object_set_data(GTK_OBJECT(node_item), "snode", node);

  }
}

static void layout_adj_changed(GtkWidget *widget, Breadboard_Window *bbw)
{
    if(GTK_LAYOUT (bbw->layout)->bin_window==0)
	return;

    if(bbw->layout_pixmap==0)
    {
	puts("bbw.c: no pixmap4!");
	return;
    }

    int xoffset=0, yoffset=0;
    
    GtkAdjustment *xadj, *yadj;
    xadj = gtk_layout_get_hadjustment (GTK_LAYOUT(bbw->layout));
    yadj = gtk_layout_get_vadjustment (GTK_LAYOUT(bbw->layout));
    xoffset = (int) GTK_ADJUSTMENT(xadj)->value;
    yoffset = (int) GTK_ADJUSTMENT(yadj)->value;

    gdk_draw_pixmap(GTK_LAYOUT (bbw->layout)->bin_window,
		    bbw->window->style->white_gc,
		    bbw->layout_pixmap,
		    xoffset, yoffset,
#if GTK_MAJOR_VERSION >= 2
		    xoffset, yoffset,
#else
		    0, 0,
#endif
		    bbw->layout->allocation.width,
		    bbw->layout->allocation.height);
}

static gboolean layout_expose(GtkWidget *widget, GdkEventExpose *event, Breadboard_Window *bbw)
{

    if(bbw->layout_pixmap==0)
    {
	puts("bbw.c: no pixmap5!");
	return 0;
    }

    layout_adj_changed(widget, bbw);

    return 0;
}

static GtkWidget *bb_vbox(GtkWidget *window, const char *name)
{
  GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
  gtk_widget_ref (vbox);
  gtk_object_set_data_full (GTK_OBJECT (window), name, vbox,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (vbox);

  return vbox;
}

static GtkWidget *bb_hbox(GtkWidget *window, const char *name)
{
  GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
  gtk_widget_ref (hbox);
  gtk_object_set_data_full (GTK_OBJECT (window), name, hbox,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (hbox);

  return hbox;
}

GtkWidget* Breadboard_Window::add_button(const char *label, const char *name,
					 GtkSignalFunc f, GtkWidget *box)
{

  GtkWidget *button = gtk_button_new_with_label (label);
  gtk_widget_ref (button);
  gtk_object_set_data_full (GTK_OBJECT (window), name, button,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (button);
  gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
  gtk_signal_connect(GTK_OBJECT(button),
		     "clicked",
		     f,
		     this);

  return button;
}

void Breadboard_Window::Build(void)
{
  if(bIsBuilt)
    return;

  if(!enabled)
    return;

  GtkWidget *hpaned1;
  GtkWidget *vbox9;
  GtkWidget *vbox13;
  GtkWidget *scrolledwindow4;
  GtkWidget *viewport9;
  GtkWidget *tree1;
  GtkWidget *hbox12;
  GtkWidget *hbox15;
  GtkWidget *vbox12;
  GtkWidget *scrolledwindow3;
  GtkWidget *viewport8;
  GtkWidget *hbox11;
  GtkWidget *pic_settings_entry;
  GtkWidget *pic_settings_button;

  GtkWidget *vbox11;
  GtkWidget *scrolledwindow2;
  GtkWidget *viewport7;
  GtkWidget *hbox10;
  GtkWidget *vbox14;
  GtkWidget *hbox13;

  GtkWidget *vbox10;
  GtkWidget *scrolledwindow1;
  GtkWidget *viewport6;
  GtkWidget *hbox9;
  GtkWidget *hbox14;
  GtkWidget *scrolledwindow5;

  GdkColormap *colormap = gdk_colormap_get_system();

  gdk_color_parse("red",&high_output_color);
  gdk_color_parse("darkgreen",&low_output_color);
  gdk_color_parse("black",&black_color);
  gdk_color_parse("gray",&gray_color);
  
  gdk_colormap_alloc_color(colormap, &high_output_color,FALSE,TRUE);
  gdk_colormap_alloc_color(colormap, &low_output_color,FALSE,TRUE);
  gdk_colormap_alloc_color(colormap, &black_color,FALSE,TRUE);


  //
  // Top level window
  //

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_object_set_data (GTK_OBJECT (window), "window", window);
  gtk_window_set_title (GTK_WINDOW (window), "Breadboard [Currently in development]");

  //
  // Horizontal pane 
  //

  hpaned1 = gtk_hpaned_new ();
  gtk_widget_ref (hpaned1);
  gtk_object_set_data_full (GTK_OBJECT (window), "hpaned1", hpaned1,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (hpaned1);
  gtk_container_add (GTK_CONTAINER (window), hpaned1);
  gtk_paned_set_position (GTK_PANED (hpaned1), 196);

  // vbox9 holds the left pane.
  vbox9 = bb_vbox(window, "vbox9");
  gtk_paned_pack1 (GTK_PANED (hpaned1), vbox9, FALSE, TRUE);

  vbox13 = bb_vbox(window, "vbox13");
  gtk_box_pack_start (GTK_BOX (vbox9), vbox13, TRUE, TRUE, 2);

  scrolledwindow4 = gtk_scrolled_window_new (0, 0);
  gtk_widget_ref (scrolledwindow4);
  gtk_object_set_data_full (GTK_OBJECT (window), "scrolledwindow4", scrolledwindow4,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (scrolledwindow4);
  gtk_box_pack_start (GTK_BOX (vbox13), scrolledwindow4, TRUE, TRUE, 0);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow4), 
				  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  viewport9 = gtk_viewport_new (0, 0);
  gtk_widget_ref (viewport9);
  gtk_object_set_data_full (GTK_OBJECT (window), "viewport9", viewport9,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (viewport9);
  gtk_container_add (GTK_CONTAINER (scrolledwindow4), viewport9);

  tree = tree1 = gtk_tree_new ();
  gtk_widget_ref (tree1);
  gtk_object_set_data_full (GTK_OBJECT (window), "tree1", tree1,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_tree_set_selection_mode (GTK_TREE(tree),GTK_SELECTION_BROWSE);
  gtk_widget_show (tree1);
  gtk_container_add (GTK_CONTAINER (viewport9), tree1);

  hbox12 = bb_hbox(window, "hbox12");
  gtk_box_pack_start (GTK_BOX (vbox13), hbox12, FALSE, FALSE, 0);

  add_button("Add node","button5", (GtkSignalFunc) add_new_snode, hbox12);
  add_button("Add module","button6", (GtkSignalFunc) add_module, hbox12);
  add_button("Add library","button7", (GtkSignalFunc) add_library, hbox12);

  hbox15 = bb_hbox(window, "hbox15");
  gtk_box_pack_start (GTK_BOX (vbox13), hbox15, FALSE, FALSE, 0);

  add_button("Trace all","button25", (GtkSignalFunc) trace_all, hbox15);
  add_button("Clear traces","button26", (GtkSignalFunc) clear_traces, hbox15);




  pic_frame = gtk_frame_new ("PIC settings");
  gtk_widget_ref (pic_frame);
  gtk_object_set_data_full (GTK_OBJECT (window), "pic_frame", pic_frame,
                            (GtkDestroyNotify) gtk_widget_unref);
  //  gtk_widget_show (pic_frame);
  gtk_box_pack_start (GTK_BOX (vbox9), pic_frame, TRUE, TRUE, 0);
  vbox12 = bb_vbox(window, "vbox12");
  gtk_container_add (GTK_CONTAINER (pic_frame), vbox12);

  scrolledwindow3 = gtk_scrolled_window_new (0, 0);
  gtk_widget_ref (scrolledwindow3);
  gtk_object_set_data_full (GTK_OBJECT (window), "scrolledwindow3", scrolledwindow3,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (scrolledwindow3);
  gtk_box_pack_start (GTK_BOX (vbox12), scrolledwindow3, TRUE, TRUE, 0);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow3), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  viewport8 = gtk_viewport_new (0, 0);
  gtk_widget_ref (viewport8);
  gtk_object_set_data_full (GTK_OBJECT (window), "viewport8", viewport8,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (viewport8);
  gtk_container_add (GTK_CONTAINER (scrolledwindow3), viewport8);

  pic_settings_clist = gtk_clist_new (1);
  gtk_widget_ref (pic_settings_clist);
  gtk_object_set_data_full (GTK_OBJECT (window), "pic_settings_clist", pic_settings_clist,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (pic_settings_clist);
  gtk_container_add (GTK_CONTAINER (viewport8), pic_settings_clist);

  hbox11 = bb_hbox(window, "hbox11");
  gtk_box_pack_start (GTK_BOX (vbox12), hbox11, FALSE, FALSE, 0);

  pic_settings_entry = gtk_entry_new ();
  gtk_widget_ref (pic_settings_entry);
  gtk_object_set_data_full (GTK_OBJECT (window), "pic_settings_entry", pic_settings_entry,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (pic_settings_entry);
  gtk_box_pack_start (GTK_BOX (hbox11), pic_settings_entry, FALSE, FALSE, 0);

  pic_settings_button = gtk_button_new_with_label ("Set");
  gtk_widget_ref (pic_settings_button);
  gtk_object_set_data_full (GTK_OBJECT (window), "pic_settings_button", pic_settings_button,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (pic_settings_button);
  gtk_box_pack_start (GTK_BOX (hbox11), pic_settings_button, FALSE, FALSE, 0);





  node_frame = gtk_frame_new ("Node connections");
  gtk_widget_ref (node_frame);
  gtk_object_set_data_full (GTK_OBJECT (window), "node_frame", node_frame,
                            (GtkDestroyNotify) gtk_widget_unref);
  //  gtk_widget_show (node_frame);
  gtk_box_pack_start (GTK_BOX (vbox9), node_frame, TRUE, TRUE, 0);

  vbox11 = gtk_vbox_new (FALSE, 0);
  gtk_widget_ref (vbox11);
  gtk_object_set_data_full (GTK_OBJECT (window), "vbox11", vbox11,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (vbox11);
  gtk_container_add (GTK_CONTAINER (node_frame), vbox11);

  scrolledwindow2 = gtk_scrolled_window_new (0, 0);
  gtk_widget_ref (scrolledwindow2);
  gtk_object_set_data_full (GTK_OBJECT (window), "scrolledwindow2", scrolledwindow2,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (scrolledwindow2);
  gtk_box_pack_start (GTK_BOX (vbox11), scrolledwindow2, TRUE, TRUE, 0);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow2), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  viewport7 = gtk_viewport_new (0, 0);
  gtk_widget_ref (viewport7);
  gtk_object_set_data_full (GTK_OBJECT (window), "viewport7", viewport7,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (viewport7);
  gtk_container_add (GTK_CONTAINER (scrolledwindow2), viewport7);

  node_clist = gtk_clist_new (1);
  gtk_widget_ref (node_clist);
  gtk_object_set_data_full (GTK_OBJECT (window), "node_clist", node_clist,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (node_clist);
  gtk_container_add (GTK_CONTAINER (viewport7), node_clist);
  gtk_signal_connect(GTK_OBJECT(node_clist),
		     "select_row",
		     (GtkSignalFunc) node_clist_cb,
		     (gpointer)this);

  hbox10 = bb_hbox(window, "hbox10");
  gtk_box_pack_start (GTK_BOX (vbox11), hbox10, FALSE, FALSE, 0);

  add_button("Remove stimulus","rsb", (GtkSignalFunc) remove_node_stimulus, hbox10);
  add_button("Remove node","rnb", (GtkSignalFunc) remove_node, hbox10);

  stimulus_frame = gtk_frame_new ("Stimulus settings");
  gtk_widget_ref (stimulus_frame);
  gtk_object_set_data_full (GTK_OBJECT (window), "stimulus_frame", stimulus_frame,
                            (GtkDestroyNotify) gtk_widget_unref);
  //  gtk_widget_show (stimulus_frame);
  gtk_box_pack_start (GTK_BOX (vbox9), stimulus_frame, FALSE, FALSE, 0);

  vbox14 = gtk_vbox_new (FALSE, 0);
  gtk_widget_ref (vbox14);
  gtk_object_set_data_full (GTK_OBJECT (window), "vbox14", vbox14,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (vbox14);
  gtk_container_add (GTK_CONTAINER (stimulus_frame), vbox14);


  stimulus_settings_label=gtk_label_new("");
  gtk_widget_show(stimulus_settings_label);
  gtk_box_pack_start(GTK_BOX(vbox14), stimulus_settings_label, FALSE,FALSE,0);

  hbox13 = bb_hbox(window, "hbox13");
  gtk_box_pack_start (GTK_BOX (vbox14), hbox13, FALSE, FALSE, 0);

  add_button("Connect stimulus to node","sanb", (GtkSignalFunc) stimulus_add_node, hbox13);




  module_frame = gtk_frame_new ("Module settings");
  gtk_widget_ref (module_frame);
  gtk_object_set_data_full (GTK_OBJECT (window), "module_frame", module_frame,
                            (GtkDestroyNotify) gtk_widget_unref);
  //  gtk_widget_show (module_frame);
  gtk_box_pack_start (GTK_BOX (vbox9), module_frame, TRUE, TRUE, 0);

  vbox10 = gtk_vbox_new (FALSE, 0);
  gtk_widget_ref (vbox10);
  gtk_object_set_data_full (GTK_OBJECT (window), "vbox10", vbox10,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (vbox10);
  gtk_container_add (GTK_CONTAINER (module_frame), vbox10);

  scrolledwindow1 = gtk_scrolled_window_new (0, 0);
  gtk_widget_ref (scrolledwindow1);
  gtk_object_set_data_full (GTK_OBJECT (window), "scrolledwindow1", scrolledwindow1,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (scrolledwindow1);
  gtk_box_pack_start (GTK_BOX (vbox10), scrolledwindow1, TRUE, TRUE, 0);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  viewport6 = gtk_viewport_new (0, 0);
  gtk_widget_ref (viewport6);
  gtk_object_set_data_full (GTK_OBJECT (window), "viewport6", viewport6,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (viewport6);
  gtk_container_add (GTK_CONTAINER (scrolledwindow1), viewport6);

  attribute_clist = gtk_clist_new (1);
  gtk_widget_ref (attribute_clist);
  gtk_object_set_data_full (GTK_OBJECT (window), "attribute_clist", attribute_clist,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (attribute_clist);
  gtk_container_add (GTK_CONTAINER (viewport6), attribute_clist);
  gtk_signal_connect(GTK_OBJECT(attribute_clist),
		  "select_row",
		  (GtkSignalFunc)settings_clist_cb,
		  (gpointer)this);

  hbox9 = bb_hbox(window, "hbox9");
  gtk_box_pack_start (GTK_BOX (vbox10), hbox9, FALSE, FALSE, 0);

  attribute_entry = gtk_entry_new ();
  gtk_widget_ref (attribute_entry);
  gtk_object_set_data_full (GTK_OBJECT (window), "attribute_entry", attribute_entry,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (attribute_entry);
  gtk_box_pack_start (GTK_BOX (hbox9), attribute_entry, FALSE, FALSE, 0);
  gtk_signal_connect(GTK_OBJECT(attribute_entry),
		  "activate",
		  (GtkSignalFunc) settings_set_cb,
		  this);

  add_button("Set","attribute_button", (GtkSignalFunc) settings_set_cb, hbox9);

  hbox14 = bb_hbox(window, "hbox14");
  gtk_box_pack_start (GTK_BOX (vbox10), hbox14, FALSE, FALSE, 0);

  add_button("Remove module","remove_module_button", (GtkSignalFunc) remove_module, hbox14);
  add_button("Save Configuration ...","save_stc_button", (GtkSignalFunc) save_stc, vbox9);

  scrolledwindow5 = gtk_scrolled_window_new (0, 0);
  gtk_widget_ref (scrolledwindow5);
  gtk_object_set_data_full (GTK_OBJECT (window), "scrolledwindow5", scrolledwindow5,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_widget_show (scrolledwindow5);
  gtk_paned_pack2 (GTK_PANED (hpaned1), scrolledwindow5, TRUE, TRUE);

  vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrolledwindow5));
  hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scrolledwindow5));

  layout = gtk_layout_new (hadj, vadj);
  gtk_widget_ref (layout);
  gtk_object_set_data_full (GTK_OBJECT (window), "layout", layout,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_container_add (GTK_CONTAINER (scrolledwindow5), layout);
  gtk_layout_set_size (GTK_LAYOUT (layout), LAYOUTSIZE_X, LAYOUTSIZE_Y);
  GTK_ADJUSTMENT (GTK_LAYOUT (layout)->hadjustment)->step_increment = 10;
  GTK_ADJUSTMENT (GTK_LAYOUT (layout)->vadjustment)->step_increment = 10;
  gtk_widget_set_events(layout,
			gtk_widget_get_events(layout)|
			GDK_BUTTON_PRESS_MASK |
			GDK_BUTTON_MOTION_MASK |
			GDK_BUTTON_RELEASE_MASK);
  gtk_signal_connect(GTK_OBJECT(layout),"motion-notify-event",
		     GTK_SIGNAL_FUNC(pointer_cb),this);
  gtk_signal_connect(GTK_OBJECT(layout),"button_press_event",
		     GTK_SIGNAL_FUNC(pointer_cb),this);
  gtk_signal_connect(GTK_OBJECT(layout),"button_release_event",
		     GTK_SIGNAL_FUNC(pointer_cb),this);
  gtk_signal_connect(GTK_OBJECT(layout),"expose_event",
		     (GtkSignalFunc) layout_expose,this);

  GtkAdjustment *xadj, *yadj;
  xadj = gtk_layout_get_hadjustment (GTK_LAYOUT(layout));
  yadj = gtk_layout_get_vadjustment (GTK_LAYOUT(layout));
  gtk_signal_connect(GTK_OBJECT(xadj),"value_changed",
		     (GtkSignalFunc) layout_adj_changed,this);
  gtk_signal_connect(GTK_OBJECT(yadj),"value_changed",
		     (GtkSignalFunc) layout_adj_changed,this);

  gtk_widget_set_app_paintable(layout, TRUE);
  gtk_widget_show (layout);


  //printf("bb %s:%d, w=%d, h=%d\n",__FUNCTION__,__LINE__,width,height);
  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_object (GTK_OBJECT (window), "destroy",
  //			     GTK_SIGNAL_FUNC (gtk_widget_destroyed), GTK_OBJECT(window));
  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
		      GTK_SIGNAL_FUNC(delete_event), (gpointer)this);
  gtk_signal_connect_after(GTK_OBJECT(window), "configure_event",
			   GTK_SIGNAL_FUNC(gui_object_configure_event),this);


  gtk_widget_realize(window);

  pinname_gc=gdk_gc_new(window->window);

  case_gc=gdk_gc_new(window->window);
  gdk_gc_set_line_attributes(case_gc,CASELINEWIDTH,GDK_LINE_SOLID,GDK_CAP_ROUND,GDK_JOIN_ROUND);

#if GTK_MAJOR_VERSION >= 2
  pinstatefont = pango_font_description_from_string("Courier Bold 8");
  pinnamefont = pango_font_description_from_string("Courier Bold 8");
#else
  pinstatefont = gdk_fontset_load ("-adobe-courier-bold-r-*-*-*-80-*-*-*-*-*-*");
  pinnamefont = gdk_fontset_load ("-adobe-courier-bold-r-*-*-*-80-*-*-*-*-*-*");
#endif
  pinline_gc=gdk_gc_new(window->window);
  g_assert(pinline_gc!=0);
  gdk_gc_set_line_attributes(pinline_gc,PINLINEWIDTH,GDK_LINE_SOLID,GDK_CAP_ROUND,GDK_JOIN_ROUND);

  layout_pixmap = gdk_pixmap_new(window->window,
				      LAYOUTSIZE_X,
				      LAYOUTSIZE_Y,
				      -1);

#if GTK_MAJOR_VERSION >= 2
  pinnameheight = gdk_string_height (gdk_font_from_description(pinnamefont),"9y");
#else
  pinnameheight = gdk_string_height (pinnamefont,"9y");
#endif

  if(pinspacing<pinnameheight)
    pinspacing=pinnameheight+2;

  if(pinspacing%ROUTE_RES)
    {
      pinspacing-=pinspacing%ROUTE_RES;
      pinspacing+=ROUTE_RES;
    }


  GtkWidget *tree_item;
  struct gui_node *gn;

  gn = (struct gui_node *) malloc(sizeof(*gn));
  gn->bbw=this;
  gn->node=0; // indicates that this is the root node.
  tree_item = gtk_tree_item_new_with_label ("nodes");
  //    gtk_signal_connect(GTK_OBJECT(tree_item),
  //		       "select",
  //		       (GtkSignalFunc) treeselect_node,
  //		       gn);
  gtk_widget_show(tree_item);
  gtk_tree_append(GTK_TREE(tree), tree_item);
  node_tree= gtk_tree_new();
  gtk_widget_show(node_tree);
  gtk_tree_item_set_subtree(GTK_TREE_ITEM(tree_item), node_tree);
  gtk_object_set_data(GTK_OBJECT(node_tree), "root_of_nodes", gn);

  bIsBuilt = true;

  UpdateMenuItem();

  draw_nodes(this);

  // Look module list
  Symbol_Table::module_symbol_iterator mi = get_symbol_table().beginModuleSymbol();
  Symbol_Table::module_symbol_iterator itModEnd = get_symbol_table().endModuleSymbol();
  for(mi = get_symbol_table().beginModuleSymbol();
      mi!=itModEnd;
      mi++)
  {
  	NewModule((*mi)->get_module());
  }

  // Loop node list
  Symbol_Table &ST = get_symbol_table();
  Symbol_Table::node_symbol_iterator it;
  Symbol_Table::node_symbol_iterator itEnd = ST.endNodeSymbol();
  for(it = ST.beginNodeSymbol(); it != itEnd; it++) {
    Stimulus_Node *node = (*it)->getNode();
    assert(node != NULL);
    if(node != NULL) {
    	NodeConfigurationChanged(node);
    }
  }
  gtk_widget_show(window);

  Update();
}


Breadboard_Window::Breadboard_Window(GUI_Processor *_gp)
{
  menu = "<main>/Windows/Breadboard";

  set_name("pinout");
  wc = WC_misc;
  wt = WT_breadboard_window;
  window = 0;

  pinstatefont = 0;
  pinnamefont = 0;
  pinname_gc = 0;
  pinline_gc = 0;
  case_gc = 0;
  node_tree = 0;

  modules=0;

  node_clist=0;

  stimulus_settings_label=0;

  stimulus_add_node_button=0;

  selected_node=0;
  selected_pin=0;
  selected_module=0;

  hadj = 0;
  vadj = 0;

  layout_pixmap=0;

  gp = _gp;

  if(!get_config())
    printf("warning: %s\n",__FUNCTION__);

  //printf("bb %s:%d, w=%d, h=%d\n",__FUNCTION__,__LINE__,width,height);

  if(enabled)
    Build();

}

#endif // HAVE_GUI


syntax highlighted by Code2HTML, v. 0.9.1