#include "xdrv.h"
#include <InterViews/enter-scope.h>
#include <Dispatch/enter-scope.h>
#include <InterViews/event.h>
#include <InterViews/input.h>
#include <InterViews/layout.h>
#include <InterViews/canvas.h>
#include <InterViews/geometry.h>
#include <InterViews/brush.h>
#include <InterViews/canvas.h>
#include <InterViews/glyph.h>
#include <InterViews/monoglyph.h>
#include <InterViews/polyglyph.h>
#include <InterViews/action.h>
#include <InterViews/action.h>
#include <InterViews/background.h>
#include <InterViews/label.h>
#include <InterViews/tile.h>
#include <InterViews/telltale.h>
#include <InterViews/color.h>
#include <InterViews/window.h>
#include <IV-look/button.h>
#include <IV-look/kit.h>
#include <InterViews/cursor.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
#include <stdlib.h>
#include "portable.h"
#include "netdescg.h"
#include "netplot.h"
#include "dsp_app.h"
#include "cgidbg.h"
#include "mkstr.h"
#include "xgui.h"
#include "help.h"
#include "usercom.h"
#include "editnet.h"
#include "mpacket.h"
#include "remcom.h"
#include "genmenu.h"
#include <InterViews/enter-scope.h>
#include <OS/leave-scope.h>
#include <OS/enter-scope.h>
#include "blinker.h"
#include "bound_val.h"
#include "ObjProGen/dspconst.h"

const Coord NodeGlyph::hmargin_size = 10.0 ;
const Coord NodeGlyph::vmargin_size = 5.0 ;

// #define TEST_ALLOC
#ifdef TEST_ALLOC
void TestAlloc(const char * Msg=0)
{
	static int avoid_recursion = 0 ;
	if (avoid_recursion) return ;
	avoid_recursion = 1 ;
    LogOut << "Enter TestAlloc(\"" ;
    if (Msg) LogOut << Msg ;
    LogOut << "\")\n" ;
    const Number = 77 ;
    const big = 119 ;
    double * x_double = new double[big];
    for (int i = 0 ; i < big ;i ++) x_double[i] = .45989568958654598e13 ;
    delete x_double ;
    char * Tests[Number];
    EdibleGlyph * strs[Number] ;
    for (i = 0 ; i < Number; i++) {
        int Limit = 2*i+4 ;
        char * Temp ;
        strs[i] = new EdibleGlyph(VerticalGlyphs::node_);
        Tests[i] = Temp = new char[Limit];
        for (int j = 0 ; j < Limit ; j++) Temp[j] = rand();
    }
    for (i = 0 ; i < Number; i++) {
        delete Tests[i] ;
        delete strs[Number -i -1] ;
    }
    x_double = new double[big];
    for (i = 0 ; i < big ;i ++) x_double[i] = .7333333;
    delete  x_double ;
    LogOut << "Exit TestAlloc\n" ;
	avoid_recursion = 0 ;
}

#endif

static void draw_arrow(Canvas * c, Coord fx, Coord fy, Coord tx, Coord ty)
{
	static double arrow_head_length = 5. * sqrt(2.);
	static double arrow_angle = atan(4./6.);
	if (fx == 0.0 || fy == 0.0 || tx == 0.0 || ty == 0.0) return ;
/*
 *	LogOut << "draw_arrow " << fx << ", " << fy << " to " << tx << ", " << ty
 *		<< "\n" ;
 */
	const Color * color = DspApplication::black();

	double dx = fx - tx ;
	double dy = fy - ty ;
	double angle = DoublePi * .5 ;
	double arrow_ang = arrow_angle ;
	if (dy != 0) angle = atan(dx/dy);
	int y_dir = 1 ;
	if (dy <= 0 ) y_dir = -1 ;
	double cos_ang_plus = cos(angle+arrow_ang);
	double sin_ang_plus = sin(angle+arrow_ang);
	double cos_ang_minus = cos(angle-arrow_ang);
	double sin_ang_minus = sin(angle-arrow_ang);
/*
 *	LogOut << "angle = " << angle << ", cosP = " << cos_ang_plus <<
 *		", sinP = " << sin_ang_plus << "\n" ;
 *	LogOut << "cosM = " << cos_ang_minus <<
 *		", sinM = " << sin_ang_minus << "\n" ;
 */
	tx += y_dir * sin(angle) * 20 ;
	ty += y_dir * cos(angle) * 20 ;

	c->move_to(fx,fy);
	c->line_to(tx,ty);
	c->stroke(color,DspApplication::brush());
	
	c->line_to(tx+ y_dir * arrow_head_length * sin_ang_plus,
		ty+ y_dir * arrow_head_length * cos_ang_plus);
	c->line_to(tx+ y_dir *  arrow_head_length * sin_ang_minus,
		ty+ y_dir * arrow_head_length * cos_ang_minus);
    c->line_to(tx,ty);
    c->fill(color);
	c->stroke(DspApplication::black(),DspApplication::brush());
}


implementEditLinearList(HorizontalPosition)
implementEditLinearList(VerticalPosition)

class BufferWidget : public ActiveHandler {
	NodeConnector & the_connector ;
	const Color& color ;
public:
	BufferWidget(WidgetKit&kit, Style * style, NodeConnector& conn,
		const Color * col, const char *label);
	void draw(Canvas*, const Allocation &) const ;
	virtual void request(Requisition& r) const;
	static Coord width() {return the_width;}
	static Coord height() {return the_height;}
	// virtual void release(const Event&);
	virtual void press(const Event&e) {the_connector.graph().press(e);}
	virtual void release(const Event&e) {the_connector.graph().release(e);}

	static const Coord the_height ;
	static const Coord the_width ;

	virtual void enter() {the_connector.base_enter();}
	virtual void leave() {the_connector.base_leave();}
};


#define default_line_width 15.0
#define default_line_spacing 5.0

class LineDraw{
public:
	static void line(Canvas*, Coord X0,Coord Y0, Coord X1,Coord Y1);
	static void right_arrow(Canvas*, Coord X0,Coord Y0, Coord X1,Coord Y1);
};

void LineDraw::line(Canvas * can,Coord X0,Coord Y0, Coord X1,Coord Y1)
{
	can->move_to(X0, Y0);
	can->line_to(X1, Y1);
	can->stroke(DspApplication::black(),DspApplication::brush());
}

static Coord arrow_half_height = 4.0 ;
void LineDraw::right_arrow(Canvas * can,Coord X0,Coord Y0, Coord X1,Coord Y1)
{
	line(can,X0,Y0,X1,Y1);
	can->line_to(X1-6.0,Y1+arrow_half_height);
	can->line_to(X1-6.0,Y1-arrow_half_height);
	can->line_to(X1,Y1);
	can->fill(DspApplication::black());
}



class LineWidget: public MonoGlyph {
	Coord width ;
public:
	const enum LineType {line, right_arrow} type ;
	LineWidget(LineType typ=line, Coord Width = default_line_width);
	virtual void request (Requisition& r) const;
	virtual void draw(Canvas*, const Allocation&) const ;
};

LineWidget::LineWidget(LineType typ,Coord w):
	MonoGlyph(),
	width(w),
	type(typ)
{
}

void LineWidget::draw(Canvas* can, const Allocation& all) const
{
	Coord y = (all.top() + all.bottom()) *.5 ;
	switch (type) {
case line:
		LineDraw::line(can,all.left(),y,all.right(),y);
		break ;
case right_arrow:
		LineDraw::right_arrow(can,all.left(),y,all.right(),y);
		break ;
	}
}

void LineWidget::request (Requisition& req) const
{
	Requirement& rx = req.x_requirement();
	Requirement& ry = req.y_requirement();

	rx.natural(width);
	rx.stretch(0.0);
	rx.shrink(0.0);
	rx.alignment(0.0);

	ry.stretch(0.0);
	ry.shrink(0.0);
	// ry.natural(default_line_spacing);
	ry.natural(arrow_half_height * 2. + 2.);
	ry.alignment(1.);
}


const Coord ArrowWidget::min_width = default_line_width;

ArrowWidget::ArrowWidget(WidgetKit& kit, LayoutKit& layout,
	ArrowType typ, const char *lab):
	MonoGlyph(),
	type(typ)
{
	Glyph * the_body ;
	Glyph * the_label = 0 ;
	int label_illegal = 0 ;
	if (lab) the_label = layout.vbox(kit.fancy_label(lab));
	switch(type) {
case line:
		the_body = new LineWidget(LineWidget::line,min_width);
		label_illegal = 1 ;
		break ;
case right_arrow:
		the_body = new LineWidget(LineWidget::right_arrow,min_width);
		label_illegal = 1 ;
		break ;
case right_arrow_source:
		if (!lab) break ;
		the_body = 
		layout.hbox(the_label,
			new LineWidget(LineWidget::right_arrow,min_width)
		);
		break ;
case right_arrow_sink:
		if (!lab) break ;
		the_body = layout.hbox(
			new LineWidget(LineWidget::right_arrow,min_width),
			the_label
		);
		break ;
	}
	if ((!lab) ^ label_illegal) DbgError("ArrowWidget::ctor","bad comb");
	body(
		layout.vbox(
			layout.vglue(),
			the_body,
			layout.vglue()
		)
	);
}

ArrowWidget::~ArrowWidget()
{
}

const Coord BufferWidget::the_width = 10.0 ;
const Coord BufferWidget::the_height = 12.0 ;

BufferWidget::BufferWidget(WidgetKit& kit, Style * style,  NodeConnector& node,
	const Color * col, const char * label):
		ActiveHandler(0,style),
		color(*col),
		the_connector(node)
{
	if (label) body(new Label(label, kit.font(), DspApplication::black()));
}

void BufferWidget::request(Requisition& req) const
{
	MonoGlyph::request(req);
	Requirement& rx = req.x_requirement();
	Requirement& ry = req.y_requirement();

	Coord base_x = rx.natural() ;
	// LogOut<<"base_x = "<<base_x << ", the_width = " << the_width << "\n" ;
	if (the_width > base_x + 4.) base_x = the_width ;
	else base_x += 4 ;
	rx.natural(base_x);

	Coord base_y = ry.natural() ;
	// LogOut << "base_y = "<<base_y <<", the_height = "<< the_height << "\n" ;
	if (the_height > base_y + 4. ) base_y = the_height ;
	else base_y += 4 ;
	ry.natural(base_y);
}

void BufferWidget::draw(Canvas * can, const Allocation& all) const 
{
	MonoGlyph::draw(can,all);
	Coord left=all.left() + 1.;
	Coord right=all.right() - 1. ;
	Coord top=all.top() - 1. ;
	Coord bottom=all.bottom() + 1. ;
	can->rect(left,top,right,bottom,&color, DspApplication::brush());
}


VerticalGlyphs::VerticalGlyphs(LayoutKit& layout,NetGraph& gr):
	the_vbox(layout.vbox(15)),
	the_first_visible(0),
	the_graph(gr)
{
/*
 *	LogOut << "VerticalGlyphs::ctr, count = " << the_vbox->count()
 *		<< ", number = " << edible_graph.number() << "\n" ;
 */
	Resource::ref(the_vbox) ;
}

VerticalGlyphs::~VerticalGlyphs()
{
	edible_graph.next(0);
	VerticalPosition * pos ;
	while (pos = edible_graph.next()) {
		// LogOut << "deleting pos " << (void *) pos << "\n" ;
		delete pos ;
	}
	// if (the_vbox) Resource::unref(the_vbox) ;
	the_vbox = 0 ;
}

void VerticalGlyphs::draw_connect(Canvas* c, const Allocation& a) const
{
	VerticalPosition_LinearList& e_g =
		(VerticalPosition_LinearList&) edible_graph;
	e_g.next(0);
    VerticalPosition * pos ;
    while (pos = e_g.next()) if (is_visible(pos->index()))
		pos->draw_connect(c,a) ;
}

void VerticalGlyphs::copy_vbox(LayoutKit& layout)
{
	if (!the_vbox) return ;
/*
 *	LogOut << "VerticalGlyphs::copy_vbox, count = " << !the_vbox->count() <<
 *		"\n" ;
 */
	int new_count = 5 ;
	if (the_vbox->count()) new_count = the_vbox->count();
	PolyGlyph * new_vbox = layout.vbox(new_count);
	Resource::ref(new_vbox);
	for (int i = 0 ; i < the_vbox->count() ; i++)
		new_vbox->append(the_vbox->component(i));
	Resource::unref(the_vbox);
	the_vbox = new_vbox ;
}

VerticalPosition * VerticalGlyphs::get(GlyphIndex ix)
{
	return edible_graph.get(ix);
}


void VerticalPosition::draw_connect(Canvas* c, const Allocation& a) const 
{
	HorizontalPosition_LinearList& g =
		(HorizontalPosition_LinearList&) the_glyphs ;
	g.next(0);
    HorizontalPosition* pos;
    while(pos = g.next()) pos->draw_connect(c,a);
}


void VerticalPosition::set_width_limit(Coord new_width)
{
	if (new_width < 300) new_width = 300 ;
	if (new_width > NetworkGlyph::max_width)
		new_width = NetworkGlyph::max_width ;
	NetworkGlyph::default_width= new_width;
}

VerticalPosition::VerticalPosition(LayoutKit& layout,VerticalGlyphs& vert,
	GlyphIndex ix):
		the_index(ix),
		the_patch( *new Patch(the_hbox=layout.hbox(15))),
		the_vertical_glyphs(vert)
{
	Resource::ref(&the_patch);
	if (width() < NetworkGlyph::min_width) graph().net_glyph()->width(
			NetworkGlyph::min_width);
	else if (width() >  NetworkGlyph::max_width) graph().net_glyph()->width(
		NetworkGlyph::max_width);
}

VerticalPosition::~VerticalPosition()
{
	// LogOut << "VerticalPosition::dtor\n" ;
/*
 *	the_glyphs.next(0);
 *	HorizontalPosition* pos;
 *	while(pos = the_glyphs.next()) {
 *		LogOut << "Before delete pos " << (void *) pos << "\n" ;
 *		delete pos ;
 *		LogOut << "After delete pos\n" ;
 *	}
 */
	Resource::unref(&the_patch);
	// LogOut << "VerticalPosition::dtor exit\n" ;
}

EdibleGlyph * VerticalPosition::select(const char * name)
{
	the_glyphs.next(0);
	HorizontalPosition* pos;
	while(pos = the_glyphs.next()) {
		EdibleGlyph * ret = pos->select(name);
		if (ret) return ret ;
	}
	return 0 ;
}

HorizontalPosition::HorizontalPosition(EdibleGlyph * ed, GlyphIndex ix ):
	the_cookie(ed),
	the_index(ix)
{
	// LogOut << "HorizontalPosition::ctor, this " << (void *) this << "\n" ;
}

HorizontalPosition::~HorizontalPosition()
{
	// LogOut << "HorizontalPosition::dtor " << (void *) this << "\n" ;
}

void HorizontalPosition::draw_connect(Canvas* c, const Allocation& a) const
{
	if (the_cookie) the_cookie->draw_connect(c,a);
}

EdibleGlyph * HorizontalPosition::select(const char * name)
{
	if (!the_cookie) return 0 ;
	if (the_cookie->flavor() != VerticalGlyphs::node_) return 0 ;
	NodeGlyph * the_node = the_cookie->node();
	if (!the_node) return 0 ;
	if (!strcmp(name,the_node->name())) return the_cookie ;
	return 0 ;
}


void VerticalPosition::append(EdibleGlyph& glyph)
{
	append(glyph.horizontal_position());
	glyph.vertical_position(this);
}


void VerticalPosition::replace(HorizontalPosition * pos, GlyphIndex ix)
{
	// LogOut << "VerticalPosition::replace(," << ix << ")\n" ;
	if (ix < 0) ix = pos->index() ;
	if (ix < 0 || ix >= the_glyphs.number())
		DbgError("VerticalPosition::replace","bad index");
	the_glyphs.LinearList::replace((void*)pos,ix);
	the_hbox->replace(ix,pos->cookie()->glyph());
	pos->index(ix);
	pos->cookie()->vertical_position(this);
}


int VerticalGlyphs::scroll_mode()
{
	return the_graph.scroll_mode();
}

Coord VerticalGlyphs::height() const
{
	return the_graph.height();
}

int VerticalGlyphs::is_room_for(VerticalPosition &pos)
{
	Coord total = 0 ;
	// LogOut << "VerticalGlyphs:is_room_for, height = " << height() << "\n" ;
	for (GlyphIndex ix = 0 ; ix < the_vbox->count(); ix++)
		total += DspApplication::y_req(the_vbox->component(ix));
	return total + DspApplication::y_req(pos.hbox()) < height() ;
}

void VerticalGlyphs::append(VerticalPosition &pos)
{
	// Resource::ref(&(pos.patch()));
/*
 *	 LogOut << "VerticalGlyphs::append, count = " << the_vbox->count()
 *		<< ", number = " << edible_graph.number() << "\n" ;
 */
	if (!scroll_mode()) if (!is_room_for(pos)) the_graph.set_scroll_mode();
	if (!scroll_mode()) the_vbox->append( &(pos.patch()) );
	pos.index(edible_graph.number());
	edible_graph.add(&pos);
	if (scroll_mode()) the_graph.set_scroll_size();
/*
 *	LogOut << "VerticalGlyphs::append, count = " << the_vbox->count()
 *		<< ", number = " << edible_graph.number() << "\n" ;
 */

/*
 *	if (the_vbox->count() != edible_graph.number()) DbgError(
 *		"VerticalGlyphs::append","bad count");
 */
	NetworkGlyph * gl = graph().net_glyph();
    if (gl) gl->note_auto_resize();
}

void VerticalGlyphs::update_indicies(GlyphIndex first)
{
	GlyphIndex limit = edible_graph.number();
	if (first < 0) first = 0 ;
	if (first >= limit) return ;
	for (GlyphIndex i = first ; i < limit;i++) edible_graph.get(i)->move(i);
}

void VerticalGlyphs::insert(GlyphIndex after, VerticalPosition * pos)
{
/*
 *	LogOut << "VerticalGlyphs::insert_one(" << after << ", " <<
 *		(void *) pos << ")\n" ;
 */
	if (!pos) return ;
	edible_graph.insert(after,pos);
	the_vbox->insert(after+1,pos->hbox());
	update_indicies(after);
}

void VerticalGlyphs::add_edit_node(NodeGlyph& nd)
{
	GlyphIndex size = edible_graph.number();
	for (int passes = 0; passes < 2; passes++)
	  for (GlyphIndex i = 0 ; i < size; i++) {
		VerticalPosition * check = edible_graph.get(i);
		VerticalPosition::Termination t = check->termination() ;
		if (t != VerticalPosition::unlinked_node && !passes) continue ;
		int unlinked = 0 ;
		int empty = 0 ;
		if ((unlinked = (t == VerticalPosition::unlinked_node)) ||
			(empty = (t == VerticalPosition::empty)) ||
			t == VerticalPosition::terminated_link) {
			EdibleSpace * sp = 0 ;
			if (!unlinked && ! empty) sp = new EdibleSpace(NodeGlyph::hmargin_size);
			if (!check->is_room_for(nd.glyph(),sp)) continue ;
			if (sp) check->append(sp) ;
			check->append(&nd);
			return ;
		}
	}
	add_new_line(&nd);
}

void VerticalGlyphs::add_first_fit(EdibleGlyph*a,EdibleGlyph*b,EdibleGlyph*c)
{
	// LogOut << "add_first_fit\n" ;
	GlyphIndex size = edible_graph.number();
	EdibleSpace * space = new EdibleSpace(NodeGlyph::hmargin_size);
	for (GlyphIndex i = 0 ; i < size; i++) {
		int empty = 0 ;
		VerticalPosition * check = edible_graph.get(i);
		VerticalPosition::Termination t = check->termination() ;
		if (t == VerticalPosition::terminated_link  ||
				(empty = (t == VerticalPosition::empty))) {
			if (!check->is_room_for(a,b,c,empty?0:space)) continue ;
			
			if (!empty) check->append(space);
			check->append(a);
			check->append(b);
			check->append(c);
			return ;
		}
	}
	add_new_line(a,b,c);
}

void VerticalGlyphs::add_new_line(EdibleGlyph*a,EdibleGlyph*b,EdibleGlyph*c)
{
	// LogOut << "VerticalGlyphs::add_new_line\n" ;
	VerticalPosition * pos = new VerticalPosition(graph().layout(),
		*this);
	pos->append(a);
	pos->append(b);
	pos->append(c);
	append(*pos);
	graph().resize();
}

EdibleGlyph * VerticalGlyphs::select_home()
{
	edible_graph.next(0);
	VerticalPosition * pos ;
	while (pos = edible_graph.next()) {
		EdibleGlyph * ret = pos->select_home();
		if (ret) return ret ;
	}
	return 0 ;
}

EdibleGlyph * VerticalGlyphs::select_end()
{
	VerticalPosition * pos ;
	for (GlyphIndex i = edible_graph.number() - 1; i > -1; i--) {
		pos = edible_graph.get(i);
		EdibleGlyph * ret = pos->select_end();
		if (ret) return ret ;
	}
	return 0 ;
}

GlyphIndex VerticalGlyphs::is_visible(GlyphIndex ix) const
{
	if (ix < the_first_visible) return ix ;
	GlyphIndex diff = ix - the_first_visible - visible_size() + 1  ;
/*
 *	LogOut << "VerticalGlyphs::is_visible(" << ix << ")\n" ;
 *	LogOut << "diff = " << diff << ", the_first_visible = " <<
 *		the_first_visible << ", visible_size = " << visible_size() << "\n" ; 
 */
	if (diff > 0) return the_first_visible + diff ;
	return -1 ;
}

GlyphIndex VerticalGlyphs::visible_size() const
{
	return the_graph.visible_size();
}

void VerticalGlyphs::make_visible(GlyphIndex ix)
{
	GlyphIndex adjust_to = is_visible(ix) ;
	if (adjust_to < 0) return ;
	the_graph.current_value(adjust_to);
}

void VerticalGlyphs::make_visible(EdibleGlyph * to_see)
{
	if (!to_see) return ;
	GlyphIndex to_see_index = to_see->vertical_position()->index();
	make_visible(to_see_index);
}

void VerticalGlyphs::update()
{
	if (!the_vbox) return ;
	BoundedValue * adjustable = the_graph.adjustable();
	if (!adjustable) return ;
/*
 *	LogOut << "VerticalGlyphs::update, adjustable = " << (void *) adjustable
 *		<< "\n" ;
 */

	Coord upper = adjustable->upper(Dimension_X);
	Coord length =  adjustable->length(Dimension_X);
	Coord current = adjustable->cur_lower(Dimension_X) ;
	GlyphIndex window_size =  (GlyphIndex) adjustable->window_size();
	
	GlyphIndex new_first = (GlyphIndex) (upper - window_size - current) ;
	// LogOut << "new_first = " << new_first << ", w_s = " << window_size << "\n" ;
	if (new_first < 0) new_first = 0 ;


	GlyphIndex diff = the_first_visible - new_first ;
/*
 *	LogOut << "diff = " << diff << ", l = " << window_size << ", f_v = "
 *		<< the_first_visible << "\n" ;
 */
	if (!diff) return ;
	graph().net_glyph()->do_damage();
	GlyphIndex to_replace, first_add, to_remove ;
	if (diff < 0) { // add at end
		to_replace = -diff ;
		first_add = the_first_visible + window_size  ; 
		to_remove = 0 ;
	} else { // add at start
		to_replace = diff ;
		first_add = the_first_visible -1   ;
		to_remove = window_size - 1  ;
	}
/*
 *	LogOut << "to_rep = " << to_replace << ", first_add = " << first_add <<
 *		", to_remove = " << to_remove << "\n" ;
 */
	for (GlyphIndex i = 0 ; i < to_replace ; i++) {
		the_vbox->remove(to_remove);
		VerticalPosition * pos_add = edible_graph.get(first_add);
		if (!pos_add) DbgError("VerticalGlyphs::update","no pos end");
		if (diff > 0) {
			first_add-- ;
			the_vbox->prepend(&(pos_add->patch()));
		} else {
			first_add++ ;
			the_vbox->append(&(pos_add->patch()));
		}
	}
	the_first_visible = new_first ;
	graph().patch_redraw();
}

EdibleGlyph * VerticalGlyphs::select(const char * name)
{
	edible_graph.next(0);
	VerticalPosition * pos ;
	while (pos = edible_graph.next()) {
		EdibleGlyph * ret = pos->select(name);
		if (ret) return ret ;
	}
	return 0 ;
}

void VerticalPosition::update_indicies(GlyphIndex first)
{
	GlyphIndex limit = the_glyphs.number();
	if (first < 0) first = 0 ;
	if (first >= limit) return ;
	for (GlyphIndex i = first ; i < limit;i++) the_glyphs.get(i)->move(i);
}

void VerticalPosition::Remove(GlyphIndex index, int redraw_flag)
{
	// LogOut << "VerticalPosition::Remove(" << index << ")\n" ;
	the_hbox->remove(index);
	the_glyphs.Remove(index);
	update_indicies(index);
	if (redraw_flag) redraw();
	// LogOut << "VerticalPosition::Remove(" << index << ") exit\n" ;
}

GlyphIndex VerticalPosition::insert_one(GlyphIndex after, EdibleGlyph *a)
{
/*
 *	LogOut << "VerticalPosition::insert_one(" << after << ", " <<
 *		(void *) a << ")\n" ;
 */
	if (!a) return after ;
	the_glyphs.insert(after,&(a->horizontal_position()));
	the_hbox->insert(++after,a->glyph());
	a->vertical_position(this);
	update_indicies(after);
	
	return after;
}

void VerticalPosition::insert(GlyphIndex after, EdibleGlyph *a,
	EdibleGlyph *b, EdibleGlyph *c)
{
	after = insert_one(after,a);
	after = insert_one(after,b);
	after = insert_one(after,c);
	the_vertical_glyphs.graph().resize();
}

void VerticalPosition::insert_horizontal(EdibleGlyph *a, EdibleGlyph *b,
	EdibleGlyph *c)
{
	NetGraph& g = graph();
	VerticalPosition * pos = new VerticalPosition(g.layout(),
		the_vertical_glyphs);
	pos->append(a);
	pos->append(b);
	pos->append(c);
	the_vertical_glyphs.insert(the_index,pos);
}


VerticalPosition::Termination VerticalPosition::termination()
{
	// LogOut << "VerticalPosition::termination\n" ;
	long last_index = the_glyphs.number()-1;
	if (last_index < 0) return empty ;
	// LogOut << "last = " << last_index << "\n" ;
	HorizontalPosition * last_position = the_glyphs.get(last_index) ;
	if (!last_position) DbgError("VerticalPosition::is_terminated","bad last");
	EdibleGlyph * last = last_position->cookie();
	VerticalGlyphs::Flavor flavor = last->flavor();
	switch (flavor) {
case VerticalGlyphs::node_:
		NodeGlyph * node = last->node();
		// LogOut << "Node termination for '" << node->node().name() <<  "'\n" ;
		if (!node->is_linked()) return unlinked_node;
		if (node->node().out() < 1) {
			// LogOut << "terminated link\n" ;
			return terminated_link ;
		}
		// LogOut << "open link\n" ;
		return open_link ;
case VerticalGlyphs::connector_:
case VerticalGlyphs::output_label_:
		return open_link ;
case VerticalGlyphs::input_label_:
		return terminated_link ;
case VerticalGlyphs::space_:
		// remove space if at end (it is no longer needed as a separator)
		if (!last_index) Remove(last_index,0);
		else {
			HorizontalPosition * last_node = the_glyphs.get(last_index-1) ;
			EdibleGlyph * cookie = last_node->cookie();
			if (cookie->flavor() != VerticalGlyphs::node_)
				Remove(last_index,0);
			else if (cookie->node()->node().out()) {
				Remove(last_index,0);
				return termination();
			}
		} 
		return terminated_link ;
	}
	DbgError("VerticalPosition::termination","bad case");
	return terminated_link ; // suppress compiler warning
}


NodeConnector::NodeConnector(EdibleOutputLabel & label):
		MonoGlyph(),
		EdibleGlyph(VerticalGlyphs::connector_),
		the_node(label.node()),
		driven(0),
		channel(label.channel()),
		blinking_widget(0),
		the_buf(*(label.node().buffer(channel))),
		buffer_channel(label.buffer_channel()),
		from_label(1)
{
	make_name();
	ctor_from_graph();
}

void NodeConnector::draw_connect_all(Canvas*c, const Allocation&a) const
{
	the_node.draw_connect_all(c,a,this);
}

void NodeConnector::make_name()
{
	char * buf_channel = Concatenate(DspApplication::int_string(
		buffer_channel));
	the_name=Concatenate("NodeConnector for ",the_node.name()," ",
		DspApplication::int_string(channel), " B", buf_channel);
	delete buf_channel ;
}

		
NodeConnector::NodeConnector(NodeGlyph& node_glyph):
		MonoGlyph(),
		EdibleGlyph(VerticalGlyphs::connector_),
		the_node(node_glyph.node()),
		driven(0),
		channel(node_glyph.glyph_output_connect()),
		blinking_widget(0),
		the_buf(*(node_glyph.node().buffer(channel))),
		buffer_channel(node_glyph.glyph_buffer_connect()),
		from_label(0)
{
	make_name();
	ctor_from_graph();
}

void NodeConnector::ctor_from_graph()
{
	NodeGlyph& node_glyph = *(the_node.node_glyph());
	const BufferDescription * buf = the_node.buffer(channel);
	int buf_size = buf->out();
	make_body(node_glyph.layout(),node_glyph.kit(),node_glyph.style(),buf_size);
}

NodeConnector::NodeConnector(int channel, TraverseObj& obj,
	const NodeDescription& nd, NodeDescription * the_driven):
		MonoGlyph(),
		EdibleGlyph(VerticalGlyphs::connector_),
		the_node(nd),
		driven(the_driven),
		channel(channel),
		blinking_widget(0),
		the_buf(*(nd.buffer(channel))),
		buffer_channel(nd.display_parameters()->buffer_channel()),
		from_label(0)
{
	make_name();
	// const NodeDisplayParameters& param = *(the_node.display_parameters());
	const BufferDescription * buf = the_node.buffer(channel);
	int buf_size = buf->out();
	make_body(obj.layout(),obj.kit(),obj.style(),buf_size);
}


void NodeConnector::add_from_index(Glyph * hbox,LayoutKit& layout,
	WidgetKit &kit)
{
	// LogOut << "from_label = " <<from_label<<", channel = " << channel <<"\n";
	if (!from_label) if (channel) {
		char * to_delete = Concatenate(" ",DspApplication::int_string(channel));
		 hbox->append(
			layout.vbox(
				layout.vglue(),
				kit.fancy_label(to_delete),
				layout.vglue()
			)
		);
		delete to_delete ;
	}
}
void NodeConnector::draw(Canvas*c, const Allocation&a) const 
{
	NodeConnector * This = (NodeConnector *) this ;
    This->pos.set(a);
    MonoGlyph::draw(c,a);
}

void NodeConnector::from_position(Coord& x, Coord& y) const
{
    x =  pos.center_x() + .5 * BufferWidget::the_width ;
    y =  pos.center_y();
}

void NodeConnector::make_body(LayoutKit& layout, WidgetKit &kit, Style *style,
	int buf_size)
{
	char * buf_output = 0 ;
	if (buf_size>1) buf_output = Concatenate(" ",
		DspApplication::int_string(buf_size));

	Glyph * hbox = layout.hbox(5);
	add_from_index(hbox, layout, kit);
	hbox->append(new ArrowWidget(kit,layout,ArrowWidget::right_arrow));
	Glyph * normal_widget = new BufferWidget(kit,style,*this,
		DspApplication::black(), buf_output);
	Glyph * blink_widget = new BufferWidget(kit,style,*this,
		DspApplication::cyan(), buf_output);
	blinking_widget = new Blinker(normal_widget,blink_widget,style);
	hbox->append(
		layout.vbox(
	   		layout.vglue(),
			blinking_widget,
	   		// new BufferWidget(kit,style,*this,buf_output),
	   		layout.vglue()
		)
	);
	hbox->append(new ArrowWidget(kit,layout,ArrowWidget::right_arrow));
	body(hbox);
}

NodeConnector::~NodeConnector()
{
/*
 *	LogOut << "NodeConnector::~NodeConnector(), this = 0x" << (void *) this
 *		<< "\n" ;
 */
	delete the_name ;
}

void NodeConnector::connect_buffer()
{
	the_node.node_glyph()->do_connect_buffer(channel);
}

void NodeConnector::new_buffer_size()
{
/*
 *	LogOut << "NodeConnector::new_buffer_size()\n" ;
 */
	if (buffer_channel) DbgError("NodeConnector::new_buffer_size",
		"no buffer widget");
	NodeGlyph& g = *(the_node.node_glyph());
	int new_size = the_buf.out();
	make_body(g.layout(),g.kit(),g.style(),new_size);
	if (vertical_position()) vertical_position()->redraw();
}

Coord NodeConnector::min_width = default_line_width ;

void NodeConnector::unselect()
{
	blinking_widget->stop();
}

NodeConnector * NodeConnector::select()
{
	blinking_widget->start();
	return this ;
}

void NodeConnector::show_inputs()
{
	// LogOut << "NodeConnector::show_inputs\n" ;
	// LogOut << "node_glyph() = " << (void *) the_node.node_glyph() << "\n" ;
	
	the_node.node_glyph()->show_inputs();
}

void NodeConnector::show_outputs()
{
	the_node.node_glyph()->show_outputs();
}

void NodeConnector::show_none()
{
	the_node.node_glyph()->show_none();
}


void EdibleInputLabel::draw(Canvas* c, const Allocation& a) const
{
	EdibleInputLabel * This = (EdibleInputLabel *) this ;
    This->pos.set(a);
    MonoGlyph::draw(c,a);

}

void EdibleInputLabel::to_position(Coord& x, Coord& y) const
{
    x =  pos.center_x();
    y =  pos.center_y();
}

void EdibleInputLabel::from_position(Coord& x, Coord& y) const
{
    x =  pos.center_x();
    y =  pos.center_y();
}

void EdibleInputLabel::draw_from(Canvas * c) const
{
	Coord fx,fy,tx,ty;
    // LogOut << "EdibleInputtLabel::draw_from for `" << text << "'.\n" ;
    from_position(fx,fy);
    // LogOut << "from " << fx << ", " << fy << "\n" ;
	the_node->node_glyph()->to_position(tx,ty);
    // LogOut << "to " << tx << ", " << ty << "\n" ;
    draw_arrow(c,fx,fy,tx,ty);
	if (the_next_input) the_next_input->draw_from(c);
}

void EdibleInputLabel::show_inputs()
{
	// LogOut << "EdibleInputLabel::show_inputs\n" ;
	
	the_node->node_glyph()->show_inputs();
}

void EdibleInputLabel::show_outputs()
{
	// LogOut << "EdibleInputLabel::show_outputs\n" ;
	the_node->node_glyph()->show_outputs();
}

void EdibleOutputLabel::draw_to(Canvas *c, const NodeConnector * conn) const
{
	Coord fx,fy,tx,ty;
	// LogOut << "EdibleOutputLabel::draw_to for `" << text << "'.\n" ;
	to_position(tx,ty);
	// LogOut << "to " << tx << ", " << ty << "\n" ;
	if (conn) {
		if (conn->get_channel() == the_channel) {
			conn->from_position(fx,fy);
			// LogOut << "from conn  " << tx << ", " << ty << "\n" ;
			draw_arrow(c,fx,fy,tx,ty);
		}
	} else {
		NodeGlyph * gl = the_node->node_glyph() ;
		if (the_channel != gl->glyph_output_connect()) {
			gl->from_position(fx,fy);
			// LogOut << "from glyph  " << fx << ", " << fy << "\n" ;
			draw_arrow(c,fx,fy,tx,ty);
		}
	}
	if (the_next_output) the_next_output->draw_to(c,conn);
}

void EdibleOutputLabel::to_position(Coord& x, Coord& y) const
{
    x =  pos.center_x();
    y =  pos.center_y();
}

void EdibleOutputLabel::from_position(Coord& x, Coord& y) const
{
    x =  pos.center_x();
    y =  pos.center_y();
}


void EdibleInputLabel::show_none()
{
	the_node->node_glyph()->show_none();
}

void EdibleOutputLabel::draw(Canvas* c, const Allocation& a) const
{
    EdibleOutputLabel * This = (EdibleOutputLabel *) this ;
    This->pos.set(a);
    MonoGlyph::draw(c,a);

}


void EdibleOutputLabel::show_inputs()
{
	// LogOut << "EdibleOutputLabel::show_inputs\n" ;
	
	the_node->node_glyph()->show_inputs();
}

void EdibleOutputLabel::show_outputs()
{
	// LogOut << "EdibleOutputLabel::show_outputs\n" ;
	the_node->node_glyph()->show_outputs();
}

void EdibleOutputLabel::show_none()
{
	the_node->node_glyph()->show_none();
}


NodeGlyph::NodeGlyph(NodeDescription& desc, WidgetKit& kit,
	LayoutKit& layout,Style *s):
	ActiveHandler(0,s),
	EdibleGlyph(VerticalGlyphs::node_),
	the_node(desc),
	patch(0),
	kernel(0),
	framed_kernel(0),
	input_index_glyph(0),
	the_layout(layout),
	inputs_used(0),
	outputs_used(0),
	the_kit(kit)
{
	Resource::ref(this); 
	the_node.node_glyph(this);
	// LogOut << "constructing NodeGlyph for `" << desc.name() << "'\n" ;
	output_connect = input_connect = buffer_connect = -1 ;
	const NodeDisplayParameters* param = the_node.display_parameters();
	if (param) {
		if (the_node.out() && the_node.get_buffer_array()) {
			output_connect = param->channel();
			buffer_connect = param->buffer_channel();
		}
		if (the_node.in()) input_connect = param->current_input_channel();
	}

	class_label = kit.raised_label(desc.object_class());
	Glyph * normal_label = kit.raised_label(desc.name());
	Glyph * blink_label =  new Label(desc.name(), the_kit.font(),
		DspApplication::black()->brightness(1.0));
	element_label = new Blinker(normal_label,blink_label,style());
	kernel = layout.margin( layout.vbox(
		layout.hbox(layout.hglue(),class_label,layout.hglue()),
		layout.hbox(layout.hglue(),element_label,layout.hglue())
	), 5.0) ;

	if (the_node.in() > 1) kernel = layout.lmargin(
		layout.hbox(
			layout.vbox(
				layout.vglue(),
				kit.fancy_label(DspApplication::int_string(the_node.in())),
				layout.vglue()
			), kernel
		), 5.0
	); 

	if (the_node.out() > 1) kernel = layout.rmargin(
		layout.hbox(
			kernel, layout.vbox(
				layout.vglue(),
			 	kit.fancy_label(DspApplication::int_string(the_node.out())),
				layout.vglue()
			)
		),5.0
	);


	input_index_glyph = input_index();
	normal_frame();

	Glyph * base = the_layout.vbox(
		the_layout.vglue(vmargin_size),
		&patch,
		the_layout.vglue(vmargin_size)
	) ;
/*
 *	LogOut << "`" << desc.name() << "' ,NodeGlyph ctor, in = " <<
 *		the_node.in() << ", out = " << the_node.out() << "\n" ;
 */
	body(base);
}

NodeGlyph::~NodeGlyph()
{
	// LogOut << "NodeGlyph::~NodeGlyph `" << the_node.name() << "'\n" ;
	// if (get_graph()) get_graph()->menu_keyboard()->clear_selected();
	delete inputs_used ;
	delete outputs_used;
}

void NodeGlyph::draw(Canvas* c, const Allocation& a) const
{
	NodeGlyph * This = (NodeGlyph *) this ;
    This->pos.set(a);
	ActiveHandler::draw(c,a);
}

void NodeGlyph::draw_connect_all(Canvas* c, const Allocation& a,
	const NodeConnector *conn) const
{
	the_node.draw_connect_all(c,a,conn);
}

void NodeGlyph::to_position(Coord& x, Coord& y) const
{
    x =  pos.left;
    y =  pos.center_y();
}

void NodeGlyph::from_position(Coord& x, Coord& y) const
{
    x =  pos.right;
    y =  pos.center_y();
}



int NodeGlyph::is_linked() const
{
	int ret = input_connect != -1 || output_connect != -1 ;
/*
 *	LogOut << "NodeGlyph::is_linked for `" << the_node.name() <<
 *		"' returning " << ret << "\n" ;
 */
	return ret ;
}

void NodeGlyph::draw_normal_frame()
{
	NetGraph * g = get_graph();
	if (g) {
		if (g->edit_node() == this) return ;
		if (g->second_edit_node() == this) return ;
	}
	normal_frame();
	patch.redraw();
}

void NodeGlyph::draw_bright_frame()
{
	bright_frame();
	patch.redraw();
}

Glyph * NodeGlyph::input_index()
{
	if (input_connect < 1) return 0 ;
	char * to_delete = Concatenate(" ",
		DspApplication::int_string(input_connect)," ");
	Glyph * Return = the_layout.vbox(
		the_layout.vglue(),
		the_kit.fancy_label(to_delete),
		the_layout.vglue()
	);
	delete to_delete ;
	return Return ;
}

void NodeGlyph::normal_frame()
{
	framed_kernel = the_kit.outset_frame(kernel);
	kernel_patch();
}

void NodeGlyph::kernel_patch()
{
	if (input_index_glyph) patch.body(
		the_layout.hbox(
			input_index_glyph,
			framed_kernel
		)
	);
	else patch.body(framed_kernel);
}

void NodeGlyph::bright_frame()
{
	framed_kernel = the_kit.bright_inset_frame(kernel);
	kernel_patch();
}

void NodeGlyph::request(Requisition& req) const
{
	MonoGlyph::request(req);
	Requirement& rx = req.x_requirement();
	rx.stretch(0.);
	rx.shrink(0.0);

	Requirement& ry = req.y_requirement();
	ry.stretch(0.0);
	ry.shrink(0.0);
}

void NodeGlyph::make_mark_arrays()
{
	if (!inputs_used) {
		int size = the_node.in();
		if (size) {
			inputs_used = new int[size] ;
			for (int i = 0 ; i < size ;i++) inputs_used[i] = 0 ;
		}
	}
	if (!outputs_used) {
		int size = the_node.out();
		if (size) {
			outputs_used = new int[size] ;
			for (int i = 0 ; i < size ;i++) outputs_used[i] = 0 ;
		}
	}
}

void NodeGlyph::mark_input(int i)
{
	make_mark_arrays();
	if (i >= the_node.in()) DbgError("NodeGlyph::mark_input","bad conn");
	if (i > -1) inputs_used[i] = 1 ;
}

void NodeGlyph::mark_output(int i)
{
	make_mark_arrays();
	if (i >= the_node.out()) DbgError("NodeGlyph::mark_output","bad conn");
	if (i > -1) outputs_used[i] = 1 ;
}


void NodeGlyph::mark_inputs_used()
{
	int size = the_node.in();
	if (!size) return ;
	make_mark_arrays();
	for (int i = 0 ; i < size ;i++) inputs_used[i] = 0 ;
	mark_input(input_connect);
	if (the_node.next_input()) the_node.next_input()->mark_inputs_used();
}

void NodeGlyph::mark_outputs_used()
{
	int size = the_node.out();
	if (!size) return ;
	make_mark_arrays();
	for (int i = 0 ; i < size ;i++) outputs_used[i] = 0 ;
/*
 *	LogOut << "NodeGlyph::mark_outputs_used " << output_connect <<
 *		" " << name() << "\n" ;
 */
	mark_output(output_connect);
	if (the_node.next_output()) the_node.next_output()->mark_outputs_used();
}

int NodeGlyph::input_available()
{
	int size = the_node.in() ;
	if (!size) {
		DspApplication::flush_help_info();
		*Output + OutputInfo << "Node `" << the_node.name() <<
			" is a signal node. " ;
		*Output << "It does not have any input channels to connect to.\n" ;
		DspApplication::write_help_info();
		return -1 ;
	}
	mark_inputs_used();
	for (int i = 0 ; i < size;i++) if (!inputs_used[i]) return i ;
	DspApplication::flush_help_info();
	*Output + OutputInfo << "All " << size << " input channels in node `" <<
			the_node.name() << "' are in use.\n" ;
	DspApplication::write_help_info() ;
	return -1 ;
}

void NodeGlyph::display_node_error()
{
/*
 *	LogOut << "NodeGlyph::display_node_error, in = " << the_node.in() <<
 *		", out = " << the_node.out() << "\n" ;
 */
	DspApplication::flush_help_info();
	*Output + OutputInfo << "Node `" << the_node.name() <<
		" is a display or file node. " ;
	*Output << "It does not have any output channels to connect to.\n" ;
	DspApplication::write_help_info();
}


int NodeGlyph::output_available()
{
	// LogOut << "NodeGlyph::output_available\n" ;
	int size = the_node.out() ;
	if (!size) {
		display_node_error();
		return -1 ;
	}
	mark_outputs_used();
	for (int i = 0 ; i < size;i++) if (!outputs_used[i]) return i ;
	DspApplication::flush_help_info();
	*Output + OutputInfo << "All " << size << " output channels in node `" <<
			the_node.name() << "' are in use.\n" ;
	DspApplication::write_help_info() ;
	return -1 ;

}

int NodeGlyph::buffer_available()
{
	// LogOut << "NodeGlyph::buffer_available\n" ;
	if (output_connect > -1) return output_connect ;
	if (the_node.out()) return 0 ;
	display_node_error() ;
	return -1 ;
}

int NodeGlyph::unlinked_replace()
{
	return unlinked("You cannot use it to replace a node.\n");
}

int NodeGlyph::unlinked(const char * cannot_do)
{
	if (input_connect == -1 && output_connect == -1) return 1 ;
	if (!cannot_do) return 0 ;
	DspApplication::flush_help_info();
	*Output + OutputInfo << "Node `" << the_node.name() <<
		"' is linked in a network." ;
	*Output << " " << cannot_do ;
	DspApplication::write_help_info();
	return 0 ;
}

int NodeGlyph::network_linked()
{
	const char * error = "' is not linked in the network. " ;
	if (input_connect > -1) return 1 ;
	if (!node().in()) if (output_connect > -1) error =
		"is an input node. " ;
	DspApplication::flush_help_info();
	*Output + OutputInfo << "Node `" << the_node.name() <<
		error ;
	*Output << "You cannot replace this node.\n" ;
	DspApplication::write_help_info();
	return 0 ;
}

int NodeGlyph::network_linked_or_signal()
{
	if (!the_node.in()) return 1 ;
	if (input_connect > -1) return 1 ;
	DspApplication::flush_help_info();
	*Output + OutputInfo << "Node `" << the_node.name() <<
		"' is not currently linked in the network and is not a signal node." ;
	*Output << "You cannot connect outputs from it until connecting to " ;
	*Output << "at least one of its inputs.\n" ;
	DspApplication::write_help_info();
	return 0 ;
}

void NetGraph::add_edit_node(NodeGlyph * g)
{
	if (g) glyphs.add_edit_node(*g);
}

void NetGraph::draw(Canvas* c, const Allocation& a) const
{
	DspActiveHandler::draw(c,a);
	if (do_show_label_links) glyphs.draw_connect(c,a);
}


int NetGraph::check_busy()
{
	if (!DspApplication::state()->IsBusy()) return 0 ;
	DspApplication::flush_help_info();
	*Output + OutputInfo << "The DSP process is busy executing.\n" ;
	*Output << "Network editing operations are not allowed until " ;
	*Output << "this processing completes.\n" ;
	DspApplication::write_help_info();
	return 1 ;
}

int NodeGlyph::edit_in_progress()
{
	if (graph().second_edit_node()) {
		DspApplication::flush_help_info();
		*Output + OutputInfo << "Currently linking `" <<
			graph().edit_node()->node().name() << "' with  '" <<
			graph().second_edit_node()->node().name() <<
			"'. A new link cannot be "
			<< "started until that operation is complete.\n" ;
		DspApplication::write_help_info();
		return 1 ;
	}
	return 0 ;
}

const char * NodeGlyph::connect_name(EditConnection::ConnectType type)
{
	const char * link_name = 0 ;
	switch(type) {
default:
		TheLog << "type = " << type << "\n" ;
		DbgError("NodeGlyph::connect_name","bad type");
		break ;
case EditConnection::connect_bad:
		return "bad" ;
case EditConnection::connect_replace:
		return "replace" ;
case EditConnection::connect_input:
		return "input" ;
case EditConnection::connect_output:
		return  "output" ;
case EditConnection::connect_buffer:
		return "output buffer on output" ;
	}
}

void NodeGlyph::set_edit(int channel,EditConnection::ConnectType type)
{
/*
 *	LogOut << "NodeGlyph::set_edit(" << channel << ", " <<
 *		type << ")\n" ;
 */
	first_connect.connection = type ;
	first_connect.channel = channel ;
	if (DspApplication::help_control()->Confirm()) {
		DspApplication::flush_help_info();
		*Output + OutputInfo << "Linking " << connect_name(type) <<
			" channel " << channel << " in node `" << the_node.name()
			<< "'.\n" ;
		DspApplication::write_help_info();
	}
	graph().set_edit_node(this);
}

int NodeGlyph::identical_channels(NodeGlyph * first)
{
	const char * first_name = first->node().name() ;
	if (!the_node.identical_channels(first->node())) {
		DspApplication::flush_help_info();
		*Output + OutputInfo << "You cannot replace `" <<
			 first_name << "' with `" << the_node.name() <<
			"' because they do not have the same number of input and/or " <<
			" output channels." <<
			"(You can type `escape' to deselect `" << first_name << "'.)\n" ;
		DspApplication::write_help_info();
		return 0 ;
	}
	return 1 ;
}

int NodeGlyph::compatible(EditConnection::ConnectType type)
{
	// LogOut << "NodeGlyph::compatible(" << type<< ")\n" ;
	if (type == EditConnection::connect_replace) {
		if (!unlinked_replace()) return 0 ;
	} else if (type != EditConnection::connect_input)
		if (!network_linked_or_signal()) return 0 ;
	NodeGlyph * first_node = graph().edit_node() ;
	// LogOut << "first_node = " << (void *) first_node << "\n" ;
	if (!first_node) return 1 ;
	EditConnection::ConnectType previous_type =
		first_node->first_selection().connection ;
	// LogOut << "previous_type = " << previous_type << "\n" ;
	int is_compatible = 1 ;
	switch(type) {
default:
case EditConnection::connect_bad:
		DbgError("NodeGlyph::compatible","bad type");
case EditConnection::connect_replace:
		if (!identical_channels(first_node)) return 0 ;
		return 1 ;
case EditConnection::connect_input:
		if (previous_type == EditConnection::connect_input) is_compatible = 0 ;
		break ;
case EditConnection::connect_output:
case EditConnection::connect_buffer:
		if (previous_type != EditConnection::connect_input) is_compatible = 0 ;
		break ;
	}
	if (!is_compatible) {
		const char * first_name = first_node->node().name();
		DspApplication::flush_help_info();
		*Output + OutputInfo << "You cannot connect a " <<
			connect_name(type) << " channel to the " <<
			connect_name(first_connect.connection) <<
			" channel of the previously selected node `" <<
			first_name <<
			"'. (You can type `escape' to deselect `" << first_name << "'.)\n" ;
		DspApplication::write_help_info();
	}
	return is_compatible ;
}

void NodeGlyph::set_second_edit(int channel,EditConnection::ConnectType type)
{
/*
 *	LogOut << "NodeGlyph::set_second_edit(" << channel << ", " <<
 *		type << ")\n" ;
 */
	if (edit_in_progress()) return ;
	if (!compatible(type)) return ;
	second_connect.connection = type ;
	second_connect.channel = channel ;
	if (DspApplication::help_control()->Confirm()) {
		DspApplication::flush_help_info();
		if (type == EditConnection::connect_replace) *Output + OutputInfo <<
		"Replacing with `" << the_node.name() << "'.\n" ;
		else *Output + OutputInfo << "Linking with " << connect_name(type) <<
			" channel " << channel << " in node `" << the_node.name()
			<< "'.\n" ;
		DspApplication::write_help_info();
	}
	graph().set_second_edit_node(this);
	// construct command
	char * Command = 0 ;
	if (type == EditConnection::connect_replace) {
		Command  = Concatenate(graph().network_name(),".ReplaceNode(",
			graph().edit_node()->node().name(),",",the_node.name(),
			");");
	} else {
		char * first = graph().edit_node()->make_expression();
		char * second = first ;
		char * temp = make_expression(second_connect);
		if (second_connect.connection == EditConnection::connect_input)
			second = temp ;
		else first = temp ;
		Command = Concatenate(first,">>",second,";");
		delete first ;
		delete second ;
	}
	DspApplication::root_window()->network_edit_command(Command);
	delete Command ;
}

char * NodeGlyph::make_expression(EditConnection& conn)
{
	// LogOut << " NodeGlyph::make_expression\n" ;
	int channel = conn.channel ;
	EditConnection::ConnectType type = conn.connection ;
	// LogOut << "type = " << type << ", chan = " << channel << "\n" ;
	// LogOut << "name = '" << the_node.name() << "'\n" ;
	if (channel < 0) DbgError("NodeGlyph::make_expression", "bad channel");
	char * ret  = 0 ;
	switch(type) {
default:
case EditConnection::connect_replace:
case EditConnection::connect_bad:
		DbgError("NodeGlyph::make_expression","bad type");
case EditConnection::connect_input:
		ret = Concatenate(the_node.name(),".LinkIn(",
			DspApplication::int_string(channel),")") ;
		break ;
case EditConnection::connect_output:
case EditConnection::connect_buffer:
		// signal node not yet linked is a special case
		if (!the_node.in() && output_connect == -1) ret =
			Concatenate(graph().network_name(),"+",the_node.name());
		else ret = Concatenate(graph().network_name(),".Link(",the_node.name(),
			",",DspApplication::int_string(channel),")");
		break ;
	}
/*
 *	LogOut << "NodeGlyph::make_expression, typ = " << type << " `" <<
 *		ret << "`\n" ;
 */
	return ret ;
}

void NodeGlyph::do_connect_buffer(int channel)
{
	// LogOut << "NodeGlyph::do_connect(" << channel << ")\n" ;
	if (edit_in_progress()) return ;
	if (graph().edit_node()) set_second_edit(channel,
		EditConnection::connect_buffer);
	else set_edit(channel,EditConnection::connect_buffer) ;
}

void NodeGlyph::do_link(unsigned button)
{
	EditConnection::ConnectType connect_type = EditConnection::connect_bad ;
	int connect_channel = -1;
	switch (button) {
case Event::left:
		connect_type = EditConnection::connect_input ;
		connect_channel = input_available();
		break ;
case Event::middle:
case Event::right:
		if (button == Event::middle) {
			connect_channel = output_available() ;
			connect_type = EditConnection::connect_output ;
		} else {
			connect_channel = buffer_available() ;
			connect_type = EditConnection::connect_buffer ;
		}
		break ;
default:
		return ;
	}
	if (connect_channel < 0) return ;
	set_second_edit(connect_channel,connect_type);
}

int NodeGlyph::edit_allowed()
{
	return graph().edit_allowed();
}

int NetGraph::edit_allowed()
{
	if (is_other_edit_operation_warn()) return 0 ;
	if (check_busy()) return 0 ;
	if (!is_edit_window()) {
		if (DspApplication::help_control()->Confirm()) {
			DspApplication::flush_help_info();
			*Output + OutputInfo << "Editing is not enabled in this graph.\n";
			DspApplication::write_help_info();
		}
		return 0 ;
	}
	return 1 ;
}

void NodeGlyph::do_replace(unsigned button)
{
	if (button != Event::left) return ;
	if (!edit_allowed()) return ;
	if (!unlinked_replace()) return ;
	set_second_edit(-1,EditConnection::connect_replace);
}

void NodeGlyph::replace_node(unsigned button)
{
	if (button != Event::left) return ;
	if (!edit_allowed()) return ;
	NodeGlyph * to_edit = graph().edit_node();
	// LogOut << "replace_node, to_edit = " << (void *) to_edit << "\n" ;
	if (to_edit) {
		if (to_edit->first_selection().connection ==
			EditConnection::connect_replace) do_replace(button);
		return ;
	}
	if (!network_linked()) return ;
	first_connect.connection = EditConnection::connect_replace ;
	first_connect.channel = -1 ;
	if (DspApplication::help_control()->Confirm()) {
		DspApplication::flush_help_info();
		*Output + OutputInfo << "Replacing node `" << the_node.name()
			<< "'.\n" ;
		DspApplication::write_help_info();
	}
	graph().set_edit_node(this);
}

void NodeGlyph::link(unsigned button,int chan)
{
	if (!edit_allowed()) return ;
	NodeGlyph * to_edit = graph().edit_node();
	if (to_edit) {
		if (to_edit->first_selection().connection !=
			EditConnection::connect_replace) do_link(button);
		return ;
	}
	
	EditConnection::ConnectType connect_type = EditConnection::connect_bad ;
	int connect_channel = -1;
	switch (button) {
case Event::left:
		connect_channel = input_available();
		connect_type = EditConnection::connect_input ;
		break ;
case Event::middle:
		if (!network_linked_or_signal()) return ;
		connect_channel = output_available() ;
		connect_type = EditConnection::connect_output ;
		break ;
case Event::right:
		if (!network_linked_or_signal()) return ;
		if (chan > -1) connect_channel = chan ;
		else connect_channel = buffer_available() ;
		connect_type = EditConnection::connect_buffer ;
		break ;
default:
		return ;
	}
	if (connect_channel < 0) return ;
	set_edit(connect_channel,connect_type);
}

int NodeGlyph::raise_output_windows()
{
	return DspApplication::execute_menu_command("Raise",the_node.name(),
		"instance",the_node.object_class());
}



void NodeGlyph::show_inputs()
{
	draw_bright_frame();
	const Color * rosy_brown = DspApplication::rosy_brown() ;
	color_input_labels(rosy_brown) ;
}

void NodeGlyph::show_outputs()
{
	draw_bright_frame();
	const Color * rosy_brown = DspApplication::rosy_brown() ;
	color_output_labels(rosy_brown) ;
}

void NodeGlyph::show_none()
{
	const Color * black = DspApplication::black() ;
	draw_normal_frame();
	color_input_labels(black) ;
	color_output_labels(black) ;
}

void NodeGlyph::color_input_labels(const Color * color)
{
	the_node.color_input_labels(color);
}

void NodeGlyph::color_output_labels(const Color * color)
{
	the_node.color_output_labels(color);
}



int NodeGlyph::in_network()
{
	if (the_node.in()) return input_connect > -1 ;
	return output_connect > -1 ;
}

NodeConnector * NodeGlyph::get_node_connector(int channel)
{
	// LogOut << "NodeGlyph::get_node_connector(" << channel << ")\n" ;
	if (output_connect == channel) if (!buffer_connect) {
		// LogOut << "returning the_node.displayed_buffer()\n" ;
		return the_node.displayed_buffer();
	}
	EdibleOutputLabel * next = the_node.next_output();
	if (next) return next->get_node_connector(channel);
	return 0 ;
}

int NodeGlyph::new_buffer_output(EditLink& driver, EditLink& driven)
{
/*
 *	LogOut << "NodeGlyph::new_buffer_output(" << driver.channel() << ",) `" <<
 *		the_node.name() << "'\n" ;
 */
	int new_size = the_node.new_buffer_output(driver,driven);
	// LogOut << "new_size = " << new_size << "\n" ;
	NodeConnector * conn = get_node_connector(driver.channel());
	if (conn) conn->new_buffer_size();
	return new_size - 1 ;
}

void NodeGlyph::append_to_net(EditLink& link, NodeConnector& conn,
	EdibleGlyph &to)
{
	// LogOut << "NodeGlyph::append_to_net\n" ;
	if (vertical_position()->is_room_for(conn.glyph(),to.glyph())) {
		vertical_position()->insert(horizontal_position().index(),&conn,&to);
		return ;
	}
	vertical_position()->insert(horizontal_position().index(),&conn);
	EdibleOutputLabel * start_label = new EdibleOutputLabel(graph(),link,0);
	vertical_position()->insert_horizontal(start_label,&to);
	// LogOut << "NodeGlyph::append_to_net exit\n" ;
}

void NodeGlyph::clear_connections()
{
	input_connect = output_connect = buffer_connect =  -1 ;
	node().clear_connections();
}

void NodeGlyph::clone_from(NodeGlyph& from)
{
	input_connect = from.glyph_input_connect();
	output_connect = from.glyph_output_connect();
	buffer_connect = from.glyph_buffer_connect();
	node().clone_from(from.node());
	from.clear_connections();
}

void NodeGlyph::exchange_positions(NodeGlyph& exch)
{
	// LogOut << "NodeGlyph::exchange_positions\n" ;
	Resource::ref(&exch);
	Resource::ref(this);

	HorizontalPosition& horizontal = horizontal_position();
	GlyphIndex save_index = horizontal.index();
	VerticalPosition * save_vertical = vertical_position();

	HorizontalPosition& exch_horizontal = exch.horizontal_position();
	horizontal.index(exch_horizontal.index());
	
	exch.vertical_position()->replace(&horizontal);

	exch_horizontal.index(save_index);
	save_vertical->replace(&exch_horizontal);

	graph().resize();
	Resource::unref(&exch);
	Resource::unref(this);
	// LogOut << "did resize\n" ;
}

void NodeGlyph::press_kernel(const Event& event)
{
	unsigned int the_button = event.pointer_button();
	
	int left = the_button == Event::left ;
	int right = the_button == Event::right ;
	int middle = the_button == Event::middle ;
/*
 *	LogOut << "NodeGlyph::press, " << left << " " << middle << " " << right
 *		<< ", pointer_button = " << the_button << "\n" ;
 */


	if (event.shift_is_down()) return ;
	if (event.control_is_down()) return ;

	if (left || middle) show_inputs();
	if (right || middle) show_outputs();
}

int NodeGlyph::remove_from_graph(int warn)
{
	// LogOut << "remove_from_graph `" << name() << "'\n" ;
	if (warn) {
		if (!unlinked("You cannot remove it from the dispaly.\n")) return 0 ;
	} else if (!unlinked(0)) return 0 ;
	if (graph().edit_node() == this) graph().clear_edit();
	// if (graph().second_edit_node() == this) graph().reset_edit();
	PacketHeader head(PacketNetworkEdit,CppEnums::clear_active_net,
		strlen(name())+1);
	WriteSeg->WritePacket(head,name());
	return 1 ;
}

static void disconnect_node_callback(const char * response, InputType,void * g)
{
	// LogOut << "void disconnect_node_callback:" << response << "\n" ;
	if (!g) {
		TheLog << "void disconnect_node_callback, no node\n" ;
		return ;
	}
	// LogOut << "Disconnecting `" << ((NodeGlyph *) g)->name() << ".\n" ;
	((NodeGlyph *) g)->do_disconnect(response);
}


void NodeGlyph::do_disconnect(const char * response)
{
	if (strcmp("yes",response)) {
		graph().other_edit_operation_complete();
		return ;
	}
	char * cmd = Concatenate(name(),".Unlink();");
	graph().other_action(this);
	// graph().other_edit_operation(NetGraph::disconnect_node);
	DspApplication::root_window()->network_edit_command(cmd);
	delete cmd ;
	while(!DspApplication::is_busy()) ReadSeg->ReadPacket();
	DspApplication::set_wait_for_dsp(); // needed to block commands on window
                                   // until window is deleted
}

void NodeGlyph::disconnect_node()
{
	// LogOut << "NodeGlyph::disconnect_node\n" ;
	if (!edit_allowed()) return ;
	// LogOut << "edit allowed\n" ;
	if (!graph().other_edit_operation(NetGraph::disconnect_node)) return ;
	// LogOut << "no other edit\n" ;
	graph().clear_selection();
	graph().clear_edit_node();
	StringList strings ;
	char * prmpt = Concatenate("Type `yes' to disconnect `",name(), "'");
	strings.Append(prmpt);
	strings.Append( Concatenate("and any nodes connected only through it?"));
	// LogOut << "Prompting for disconnect_node.\n" ;
	DspApplication::prompt(strings,disconnect_node_callback,"no",this,
		graph().window());
	// LogOut << "After prompt for disconnect_node.\n" ;
}

void NodeGlyph::complete_disconnect()
{
	// issue a remote command to redisplay this network
	graph().other_edit_operation_complete();
	
	graph().net_glyph()->rebuild_current();

	// char * command = Concatenate(graph().network_name(),".GraphDisplay();");
	// DspApplication::root_window()->network_edit_command(command);
	// delete command ;
}


NodeGlyph * NodeGlyph::select()
{
	element_label->start();
	return this ;
}

void NodeGlyph::unselect()
{
	element_label->stop();
}

declareActionCallback(NetGraph)
implementActionCallback(NetGraph)

void NetGraph::patch_redraw()
{
	patch->reallocate();
	patch->redraw();
}

void NetGraph::copy_vbox()
{
	glyphs.copy_vbox(the_layout);
}

/*
 * class GraphInput: public DspInputHandler {
 *	 NetGraph& graph ;
 * public:
 *	GraphInput(Glyph * g, NetGraph& gr):DspInputHandler(g,gr.style()),
 *		graph(gr){}
 *	virtual void keystroke(const Event& e) {graph.graph_keystroke(e);}
 *	virtual void release(const Event& e) {graph.release(e);}
 * };
 */

void NetGraph::make_body()
{
/*
 *	LogOut << "NetGraph::make_body, the_adjustable = " <<
 *		(void *) the_adjustable << "\n" ;
 */
/*
 *	LogOut << "(X,Y) = (" << X << ", " << Y << "), (X_req,Y_req) = (" <<
 *		X_req << ", " << Y_req << ")\n" ;
 */

	int save_edit = is_edit_window();
	if (vbox()->count()) patch->body(vbox());
	else patch->body(the_layout.vbox(the_layout.vglue(),the_layout.hglue()));
	Glyph * kernel = 0 ;
	if (the_adjustable) kernel = the_kit.inset_frame(
		new Background(
			the_layout.vbox(
				the_layout.h_natural_span(
					the_layout.hbox(
						the_layout.hglue(NodeGlyph::hmargin_size),
						patch,
						the_layout.hglue(NodeGlyph::hmargin_size)
					),width()
				),
				the_layout.vglue()
			),the_kit.background()
		)
	); else kernel = the_kit.inset_frame(
		new Background(
			the_layout.vbox(
				the_layout.hbox(
					the_layout.hglue(NodeGlyph::hmargin_size),
					patch,
					the_layout.hglue(NodeGlyph::hmargin_size)
				),
				the_layout.vglue()
			),the_kit.background()
		)
	);

	Glyph * main_body = the_adjustable_body ;
	if (!main_body) if (the_adjustable) the_adjustable_body =  main_body =
	  new Background(
		the_layout.hbox(
			the_kit.inset_frame(
				the_kit.vscroll_bar(the_adjustable)
			),
			kernel
		),
		the_kit.background()
	);
	else main_body = new Background(kernel,the_kit.background());

	PolyGlyph * top_bar = the_layout.hbox(3);
	top_bar->append(the_layout.hspace(5));
	top_bar->append(
		button = the_kit.check_box("Edit ",
			new ActionCallback(NetGraph) (
				this, &NetGraph::edit
			)
		)
	);
	top_bar->append(the_menu_keyboard);
	Glyph * the_body = the_layout.vbox(
		the_kit.inset_frame(top_bar),
		new Background(
			the_layout.variable_span(
				main_body
			), the_kit.background()
		)
	);
	body(the_body);
	// body(the_body);
	if (save_edit) set_edit();
}

NetGraph::NetGraph(NetworkDescriptionFull& desc, Style *style):
	DspActiveHandler(nil,style),
	the_kit(*WidgetKit::instance()),
	the_layout(*LayoutKit::instance()),
	the_style(*style),
	description(desc),
	glyphs(the_layout,*this),
	patch(new Patch(0)),
	to_connect(0),
	second_connect(0),
	the_selection(0),
	do_show_label_links(1),
	the_label_selection(0),
	the_mouse_selection(0),
	prefer_mouse(0),
	button(0),
	edit_flag(0),
	the_other_edit_operation(no_operation),
	the_menu_keyboard(0),
	changing_size(0),
	the_adjustable(0),
	the_adjustable_body(0),
	object(&(NodeDescription::display_network),the_kit,the_layout,style,glyphs)
{
	// LogOut << "NetGraph::NetGraph " << desc.complete() << "\n" ;
	if (desc.complete()) {
		description.network().traverse(object);
	} else {
		// make line of all nodes
		NodeDescription_LinearList& nodes = description.nodes();
		nodes.next(0);
		NodeDescription * node ;
		while (node = nodes.next()) if (node->is_linked()) {
			object.add(*node);
		}
		object.vertical_glyphs_append() ;
	}
	the_menu_keyboard = build_menu(the_kit,the_layout,&the_style,*this);
	make_body();
}

NetGraph::~NetGraph()
{
	// LogOut << "NetGraph::~NetGraph\n";
	// delete the_menu_keyboard ;
	the_menu_keyboard->clear();
}

void NetGraph::show_label_links()
{
	int save = do_show_label_links ;
	do_show_label_links = 1 ;
	if (!save) resize();
}

void NetGraph::clear_show_label_links()
{
	int save = do_show_label_links ;
	do_show_label_links = 0 ;
	if (save) resize();
}



void NetGraph::keystroke(const Event& event)
{
	int control = event.control_is_down();
	int shift = event.shift_is_down();
	long sym = event.keysym();
	sym &= 0xffffL ;
/*
 *	LogOut << "NetGraph:: keysym = 0x" <<hex<<(int)sym<< dec << ", shft = " <<
 *		shift << ", ctr = " << control << "\n" ;
 */
	switch(sym) {
default:
		the_menu_keyboard->keystroke(event);
		break ;
case XK_Escape:
		clear_selection();
		clear_edit_node();
		break ;
	}
}

void NetGraph::enter()
{
	static Cursor * center_arrow = 0 ;
	if (!center_arrow) center_arrow = new Cursor(XC_center_ptr);
	DspApplication::root_window()->cursor_window(window());
	window()->push_cursor() ;
	Cursor * use_cursor = DspApplication::state()->GetCursorType();
	if (is_edit_window())
		if (use_cursor == defaultCursor) use_cursor = center_arrow ;
	window()->cursor(use_cursor);
}



void NetGraph::set_second_edit_node(NodeGlyph * glyph)
{
	second_connect = glyph ;
	glyph->draw_bright_frame();
}

void NetGraph::set_edit_node(NodeGlyph * glyph)
{
	// LogOut << "NetGraph::set_edit_node(" << glyph << ")\n" ;
	to_connect = glyph ;
	glyph->draw_bright_frame();
}

void NetGraph::display_link(EditLink& from, EditLink& to)
{
/*
 *	Algorithm for layout:
 *		1. If this starts a new path go to new horizontal position.
 *		2. If this is a continuation of an existing path try to append
 *			to that path. If there is not enough room go to new
 *			vertical position.
 *		3. If this is a label to label link add it to the first vertical
 *			position with enough room that is NOT an uncompleted
 *			path.
 */
	int from_in_network = from.in_network();
	int append_to_from_in_network = from_in_network &&
		from.glyph.glyph_output_connect() == -1 ;
	int from_use_label = !append_to_from_in_network && from_in_network ;
	int to_use_label = to.in_network();
/*
 *	LogOut << "Link from `" << from.glyph.node().name() << "' to `" <<
 *		to.glyph.node().name() << "'.\n" ;
 *	LogOut << "NetGraph::display_link, from_use_label = " << from_use_label
 *		<< ", to_use_label = " << to_use_label << "\n" ;
 */
	EdibleGlyph * from_glyph = 0 ;
	EdibleGlyph * to_glyph = 0 ;
	NodeConnector * connect = 0 ;
	// Create buffer descriptor or adjust buffer size
	int buffer_channel = from.glyph.new_buffer_output(from,to);
	// LogOut << "buffer_channel = " << buffer_channel << "\n" ;

	if (from_use_label) {
		from_glyph = new EdibleOutputLabel(*this,from,buffer_channel);
	} else {
		from_glyph = &(from.glyph) ;
		from.set_channel();
		connect = new NodeConnector(from.glyph);
		from.glyph.node().displayed_buffer(connect);
	}

	if (to_use_label) {
		to_glyph = new EdibleInputLabel(*this,to);
	} else {
		to_glyph = &(to.glyph) ;
		to.set_channel();
	}

/*
 *	Start update:
 *		1. remove node glyph(s) if we are not using labels
 *		2. otherwise try to append to position of existing node
 *			if there is not room and this is termination go to
 *			first terminated line with room, otherwise start a
 *			new line. 
 */

	Resource::ref(&(from.glyph));
	Resource::ref(&(to.glyph));
	if (!from_use_label && !append_to_from_in_network) from.glyph.remove_me();
	if (!to_use_label) to.glyph.remove_me();
	int is_terminator = to_use_label || !to.glyph.node().out() ;
	if (append_to_from_in_network)
		from.glyph.append_to_net(from,*connect,*to_glyph);
	else {
		if (is_terminator) add_first_fit(from_glyph,connect,to_glyph);
		else add_new_line(from_glyph,connect,to_glyph);
	}
	Resource::unref(&(from.glyph));
	Resource::unref(&(to.glyph));
}

void NetGraph::complete_replacement(EditLink& f, EditLink& s)
{
	NodeGlyph& first = f.glyph ;
	NodeGlyph& second = s.glyph ;
	second.clone_from(first);
/*
 *	LogOut << "Exchanging `" << first.node().name() << "' with `" <<
 *		second.node().name() << "'.\n" ;
 */
	second.exchange_positions(first);
}

void NetGraph::complete_other_edit_operation()
{
/*
 *	LogOut << "NetGraph::complete_other_edit_operation, op = " <<
 *		the_other_edit_operation << "\n" ;
 */
	switch(the_other_edit_operation) {
case no_operation:
		return ;
default:
		DbgError("NetGraph::complete_other_edit_operation_complete","bad case");
case buffer_descriptor_menu:
case set_buffer_descriptor:
		break ;
case disconnect_node:
		other_action()->complete_disconnect();
		break ;
	}
}

void NetGraph::complete_edit()
{
	// LogOut << "NetGraph::complete_edit()\n" ;
	if (!edit_node()) {
		complete_other_edit_operation();
		return ;
	}
	if (!second_edit_node()) return ;
	// update nodes being edited
	EditLink * first = new EditLink(*(edit_node()),
		edit_node()->first_selection());
	EditLink * second = first ;
	EditLink * temp = new EditLink(*(second_edit_node()),
		second_edit_node()->second_selection());
	reset_edit();
	if (first->type() == EditConnection::connect_replace) {
		second = temp ;
		complete_replacement(*first, *second) ;
	} else {
		if (first->type() == EditConnection::connect_input)
			first = temp ;
		else second = temp ;
		display_link(*first,*second);
	}
	delete first ;
	delete second ;
	resize();
}

void NetGraph::error_edit()
{
	if (!edit_node()) {
		other_edit_operation_complete();
		return ;
	}
	reset_edit();
}

void NetGraph::reset_edit()
{
	NodeGlyph * temp = second_edit_node();
	if (!temp) return ;
	second_connect = 0 ;
	temp->draw_normal_frame();
	clear_edit_node();
}

void NetGraph::clear_edit_node()
{
	if (second_connect) return ;
	if (to_connect) {
		NodeGlyph * temp = to_connect ;
		to_connect = 0 ;
		temp->draw_normal_frame();
	}
}

void NetGraph::leave()
{
	// clear_edit_node();
	DspApplication::root_window()->cursor_window(0);
	window()->pop_cursor();

}

void NetGraph::press(const Event& event)
{
	if (!DspApplication::user_input_allowed()) return ;
	// LogOut << "NetGraph::press\n" ;
	prefer_mouse_selection();
	the_menu_keyboard->object_press(event);
	prefer_keyboard_selection();
}

void NetGraph::release(const Event& event)
{
	if (!DspApplication::user_input_allowed()) return ;
	// LogOut << "NetGraph::release\n" ;
	prefer_mouse_selection();
	the_menu_keyboard->object_release(event);
	prefer_keyboard_selection();
}

void NetGraph::clear_edit_base()
{
 	// LogOut<<"clear_edit_base:state is 0x" << hex << button->state()->flags() << dec << "\n" ;
	button->state()->set(TelltaleState::is_chosen,false);
	// LogOut<<"state is 0x" << hex<< button->state()->flags() << dec << "\n" ;
	button->update(0);
	edit_flag = 0 ;
}

int NetGraph::clear_edit()
{
	// LogOut << "NetGraph::clear_edit()\n" ;
	if (!is_edit_window()) return  1 ;
	if (second_edit_node()) return 0 ;
	clear_edit_base();
	return 1 ;
}

int NetGraph::is_edit_window()
{
	if (!button) return 0 ;
	// LogOut << "NetGraph::is_edit_window()\n" ;
	// LogOut << "state is 0x" << hex << button->state()->flags() << dec<<"\n" ;
	int Return = button->state()->test(TelltaleState::is_chosen) ;
	// LogOut << "NetGraph::is_edit_window() returning " << Return << "\n" ;
	return Return ;
}

void NetGraph::set_edit_base()
{
	// LogOut << "NetGraph::set_edit_base\n" ;
	button->state()->set(TelltaleState::is_chosen,true);
	button->update(0);
	edit_flag = 1 ;
}

void NetGraph::set_edit()
{
	if (is_edit_window()) return ;
	set_edit_base();
}

void NetGraph::edit()
{
	// LogOut << "NetGraph::edit, edit = " << edit_flag << "\n" ;
	if (!edit_flag) {
		if (enable_edit())
			the_menu_keyboard->key_record_act(enable_edit_key);
	} else {
		if (disable_edit()) 
			the_menu_keyboard->key_record_act(disable_edit_key);
	}
}

static void display_requisition(Glyph * g, int , int )
{
	if (!g) return ;
/*
 *	LogOut << level << " : " << index << " : x_req = " <<
 *		DspApplication::x_req(g)
 *		<< ", y_req = " << DspApplication::y_req(g) << "\n" ;
 */
}

static void dump_requisition(Glyph * g, int level = 0, int index=0)
{
	display_requisition(g,level,index);
	for (int i = 0 ; i < g->count(); i++)
		dump_requisition(g->component(i),level+1,i);
}

void  NetGraph::edit_new_node(NodeDescription * node)
{
	description.new_node(node);
	new_node(node);
}

void  NetGraph::new_node(NodeDescription * node)
{
	// Glyph * vbox = description.graph()->vbox();

	NodeGlyph * node_glyph = new NodeGlyph(*node,kit(),layout(),style());

	add_edit_node(node_glyph);
	// object.restore_last();
	// object.add(*node);
	// object.check_in_vertical_glyphs();

	// dump_requisition(vbox);
	window()->raise();
}

void  NetGraph::resize()
{
	// LogOut << "NetGraph::resize(), button = " << (void *) button << "\n" ;
	if (!button) return ;
	copy_vbox();
	make_body();
	patch_redraw();
	description.resize();
	if (net_glyph()) net_glyph()->note_auto_resize();
	// dump();
	// redraw();
}

int NetGraph::other_edit_operation(NetGraph::OtherEditOptions opt)
{
	if (is_other_edit_operation()) return 0 ;
	the_other_edit_operation = opt ;
	return 1 ;
}

int NetGraph::is_other_edit_operation_warn()
{
	if (!is_other_edit_operation()) return 0 ;
	DspApplication::flush_help_info();
	*Output + OutputInfo << "There is an active DSP command for network `"
		<< network_name() << "'.\n" << "You must wait for this to complete.\n";
	DspApplication::write_help_info();
	return 1 ;
}


void NetGraph::delete_node(NodeGlyph * node,int flag)
{
	// LogOut << "delete_node `" << node->node().name() << "'.\n" ;
	if (!node->remove_from_graph(flag)) return ;
	network_description().Remove(node->node(),1);
	node->remove_me();
	resize(); 
}

void NetGraph::add_unlinked_nodes()
{
	NodeDescription_LinearList& nodes = description.nodes();
	if (!nodes.number()) return ;
	nodes.next(0);
	NodeDescription * node ;
	NodeDescription_LinearList to_add ;
	while (node = nodes.next()) if(!node->is_linked()) to_add.add(node);
	to_add.next(0);
	while (node = to_add.next()) new_node(node);
	resize();
}

void NetGraph::clear_selection()
{
	if (selection()) selection()->unselect();
	the_selection = 0 ;
}

void NetGraph::show_selected_inputs()
{
/*
 *	LogOut << "NetGraph::show_selected_inputs(), selection() = " <<
 *		(void *) selection() <<"\n"  ;
 */
	EdibleGlyph * gl = selection();
	if (!gl) gl = the_label_selection ;
	if (!gl) return ;
	gl->show_inputs();
}

void NetGraph::show_selected_outputs()
{
	EdibleGlyph * gl = selection();
	if (!gl) gl = the_label_selection ;
	if (!gl) return ;
	gl->show_outputs();
}

void NetGraph::clear_show_selected_links()
{
	EdibleGlyph * gl = selection();
	if (!gl) gl = the_label_selection ;
	if (!gl) return ;
	gl->show_none();
}

int NetGraph::enable_edit()
{
	if (edit_flag) return 0 ;
	set_edit_base();
	if (!DspApplication::network_manager()->edit(this)) {
		// LogOut << "network manager cannot set\n" ;
		clear_edit_base();
		return 0 ;
	}
	return 1 ;
}


int NetGraph::disable_edit()
{
	if (!edit_flag) return 0 ;
	if (!DspApplication::network_manager()->edit(0)) {
		set_edit_base();
		return 0 ;
	}
	clear_edit_base();
	return 1 ;
}

void NetGraph::select_home()
{
	clear_selection();
	the_selection = glyphs.select_home();
	make_selection_visible();
}


void NetGraph::select_end()
{
	clear_selection();
	the_selection = glyphs.select_end();
	make_selection_visible();
}

void NetGraph::new_selection(EdibleGlyph * selected)
{
	if (!selected) return ;
	if (selection()) selection()->unselect();
	the_selection = selected ;
	make_selection_visible();
}

void NetGraph::select_left()
{
	if (!selection()) return ;
	new_selection(selection()->select_left());
}

void NetGraph::select_right()
{
	if (!selection()) return ;
	new_selection(selection()->select_right());
}

void NetGraph::select_up()
{
	if (!selection()) return ;
	new_selection(selection()->select_up());
}

void NetGraph::select_down()
{
	if (!selection()) return ;
	new_selection(selection()->select_down());
}

void NetGraph::select_none()
{
	clear_selection();
}

void NetGraph::connect_selected_input()
{
	if (!selection()) return ;
	if (selection()->flavor() != VerticalGlyphs::node_) return ;
	selection()->node()->link(Event::left);
	make_selection_visible();
}

void NetGraph::connect_selected_output()
{
	if (!selection()) return ;
	if (selection()->flavor() != VerticalGlyphs::node_) return ;
	selection()->node()->link(Event::middle);
	make_selection_visible();
}

void NetGraph::connect_selected_buffer()
{
/*
 *	LogOut << "NetGraph::connect_selected_buffer(), selection() = " <<
 *		(void *) selection() << "\n" ;
 */
	if (!selection()) return ;
	// LogOut << "flavor is " << selection()->flavor() << "\n" ;
	if (selection()->flavor() == VerticalGlyphs::node_) {
		// LogOut << "`" << selection()->node()->name() << "'\n" ;
		selection()->node()->link(Event::right);
		return ;
	}
	if (selection()->flavor() != VerticalGlyphs::connector_) return ;
	selection()->connector()->node().node_glyph()->link(Event::right,
		selection()->connector()->get_channel());
	make_selection_visible();
}

void NetGraph::disconnect_selected()
{
	if (!selection()) return ;
	if (selection()->flavor() != VerticalGlyphs::node_) return ;
	selection()->node()->disconnect_node() ;
}

void NetGraph::replace_selected()
{
	if (!selection()) return ;
	if (selection()->flavor() != VerticalGlyphs::node_) return ;
	selection()->node()->replace_node(Event::left);
	make_selection_visible();
}

void NetGraph::remove_selected()
{
	if (!selection()) return ;
	if (selection()->flavor() != VerticalGlyphs::node_) return ;
	if (!edit_allowed()) return ;
	delete_node(selection()->node(),1);
}


void NetGraph::selected_node_menu()
{
	if (!selection()) return ;
	if (selection()->flavor() != VerticalGlyphs::node_) return ;
	DspApplication::root_window()->do_menu_command(
		selection()->node()->node().name()
	);
}

void NetGraph::raise_output_windows()
{
	if (!selection()) return ;
	NodeGlyph * node = 0 ;
	if (selection()->flavor() == VerticalGlyphs::node_)
		node = selection()->node();
	else if (selection()->flavor() == VerticalGlyphs::connector_)
		node = selection()->connector()->node().node_glyph();
	if (!node) return ;
	node->raise_output_windows();
}


void NetGraph::selected_node_class_menu()
{
	if (!selection()) return ;
	if (selection()->flavor() != VerticalGlyphs::node_) return ;
	DspApplication::root_window()->do_menu_command(
		selection()->node()->node().object_class()
	);
}


void NetGraph::network_menu()
{
	DspApplication::root_window()->do_menu_command(
		 description.network().net_name());
}

void NetGraph::controller_menu()
{
	DspApplication::root_window()->do_menu_command(
		 description.network().controller());
}

void NetGraph::show_buffer_descriptor_menu()
{
	if (!edit_allowed()) return ;
	the_other_edit_operation = buffer_descriptor_menu ;
	if (!DspApplication::execute_menu_command("GetBufferDescriptor",
		"exec", network_name(),"instance","Network","network",
			"objects")) {
		// LogOut << "NetGraph::show_buffer_descriptor_menu failed.\n" ;
		other_edit_operation_complete();
	}
}

void NetGraph::buffer_descriptor_for_net(int size, const char * data)
{
	// LogOut << "NetGraph::buffer_descriptor_for_net.\n" ;
	if (the_other_edit_operation != buffer_descriptor_menu) return ;
	const char * net_name = data ;
	if (strcmp(net_name,network_name()))
		DbgError("NetGraph::buffer_descriptor_for_net","bad net");
	const char * buf_name = data + strlen(net_name) + 1 ;
	if (strlen(net_name) + strlen(buf_name) + 2 != size)
		DbgError("NetGraph::buffer_descriptor_for_net","bad size");
	DspApplication::root_window()->do_menu_command(buf_name);
	// LogOut << "do_menu_command(" << buf_name << ",...,\n" ;
	other_edit_operation_complete();
}

void NetGraph::set_label_selection(EdibleGlyph *g)
{
	the_label_selection = g ;
}

void NetGraph::set_mouse_selection(EdibleGlyph *g)
{
	the_mouse_selection = g ;
}


EdibleGlyph * NetGraph::selection()
{
	if (prefer_mouse) if (the_mouse_selection) return the_mouse_selection;
	if (the_selection) return the_selection ;
	return the_mouse_selection ;
}

void NetGraph::set_scroll_size()
{
	if (!the_adjustable) return ;
	changing_size = 1 ;
/*
 *	LogOut << "NetGraph::set_scroll_size,list = " <<
 *		glyphs.vertical_list().number() << ", vbox = " <<
 *		glyphs.vbox()->count() << "\n" ;
 *	LogOut << "L = " << the_adjustable->lower(Dimension_X)
 *		<< ", U = " << the_adjustable->upper(Dimension_X) << "\n" ;
 *	LogOut << "CL = " << the_adjustable->cur_lower(Dimension_X)
 *		<< ", CU = " << the_adjustable->cur_upper(Dimension_X) << "\n" ;
 */
	GlyphIndex size = glyphs.number() ;


	the_adjustable->lower_bound(0);
	the_adjustable->upper_bound(size);
	the_adjustable->current_value(the_adjustable->cur_lower(Dimension_X) +1);

/*
 *	LogOut << "L = " << the_adjustable->lower(Dimension_X)
 *		<< ", U = " << the_adjustable->upper(Dimension_X) << "\n" ;
 *	LogOut << "CL = " << the_adjustable->cur_lower(Dimension_X)
 *		<< ", CU = " << the_adjustable->cur_upper(Dimension_X) << "\n" ;
 */
	changing_size = 0 ;
}

void NetGraph::set_scroll_mode()
{
	// LogOut << "NetGraph::set_scroll_mode\n" ;
	if (the_adjustable) return ;
	BoundedValue * temp_adjustable = new BoundedValue ;
	temp_adjustable->attach(Dimension_X,this);
	temp_adjustable->window_size(glyphs.number());
	temp_adjustable->lower_bound(0);
	temp_adjustable->upper_bound(glyphs.number()+1);
	temp_adjustable->scroll_incr(1);
	temp_adjustable->page_incr(1);
	temp_adjustable->current_value(1);
/*
 *	LogOut << "window " << visible_size() << ", cu_low = " <<
 *		temp_adjustable->cur_lower(Dimension_X) << ", cur_up = " <<
 *		temp_adjustable->cur_upper(Dimension_X) << "\n" ;
 *	LogOut << "n = " << glyphs.number() << ", lower = " <<
 *		temp_adjustable->lower(Dimension_X)  << ", upper  = " <<
 *		temp_adjustable->upper(Dimension_X) << "\n" ;
 */
	// make_body();
	the_adjustable = temp_adjustable ;
}

void NetGraph::update(Observable*)
{
	if (!the_adjustable) return ;
	if (changing_size) return ;
	glyphs.update();
}

void NetGraph::make_selection_visible()
{
	if (!the_adjustable) return ;
	glyphs.make_visible(the_selection);
}

GlyphIndex NetGraph::visible_size() const
{
	if (!the_adjustable) return glyphs.number();
	return (GlyphIndex) the_adjustable->window_size();
}

GlyphIndex NetGraph::current_value()
{
	if (!the_adjustable) return 0 ;
	Coord length = the_adjustable->length(Dimension_X);
	Coord window_size = the_adjustable->window_size();
	return (GlyphIndex)
		(length - window_size - the_adjustable->cur_lower(Dimension_X)) ;
}

void NetGraph::current_value(GlyphIndex ix)
{
	if (!the_adjustable) return ;
	Coord length = the_adjustable->length(Dimension_X);
	Coord window_size = the_adjustable->window_size();

	Coord new_value = length - window_size - ix ;
/*
 *	LogOut << "NetGraph::current_value(" << ix << ")\n" ;
 *	LogOut << "w_s = " << window_size << ", l = " << length <<
 *		", setting to " << new_value << "\n" ;
 */
	the_adjustable->current_value(new_value);
}

Coord NetGraph::width() const
{
	return description.width();
}

Coord NetGraph::height() const
{
	return description.height();
}

void NetGraph::redraw_dim()
{
	NetworkGlyph * g =  net_glyph();
	if (!g) return ;
	g->check_rebuild();
}

void VerticalPosition::append(EdibleGlyph *a)
{
	if (a) append(*a);
}

void VerticalPosition::copy_hbox()
{
	if (!the_hbox) return ;
	if (!the_hbox->count()) return ;
	// if (!graph().is_edit_window()) return ;
	LayoutKit& layout = graph().layout();
	PolyGlyph * new_hbox = layout.hbox(the_hbox->count());
	for (int i = 0 ; i < the_hbox->count() ; i++)
		new_hbox->append(the_hbox->component(i));
	the_hbox = new_hbox ;
	the_patch.body(new_hbox);
}

void VerticalPosition::append(HorizontalPosition& pos)
{
/*
 *	LogOut << "pos.glyph() = " << (void *) pos.glyph() << "\n" ;
 *	LogOut << "flavor = " << pos.cookie()->flavor() << "\n" ;
 *	LogOut << "VerticalPosition::append, count = " << the_hbox->count()
 *		<< ", number = " << the_glyphs.number() << "\n" ;
 */
	int ix ;
	pos.index(ix=the_hbox->count());
	the_hbox->append(pos.glyph());
	the_hbox->modified(ix);
	the_glyphs.add(&pos);
/*
 *	LogOut << "VerticalPosition::append, count = " << the_hbox->count()
 *		<< ", number = " << the_glyphs.number() << "\n" ;
 */
	if (the_hbox->count() != the_glyphs.number() && !graph().scroll_mode()) DbgError(
			"VerticalPosition::append","bad count");
	// copy_hbox();
	// graph().copy_vbox();
	the_patch.reallocate();
	the_patch.redraw();
	graph().resize();
}

int VerticalPosition::is_room_for(EdibleGlyph * a, EdibleGlyph * b,
	EdibleGlyph * c, EdibleGlyph * d, EdibleGlyph * e, EdibleGlyph * f)
{
	Glyph *ag;
	Glyph *bg;
	Glyph *cg;
	Glyph *dg;
	Glyph *eg;
	Glyph *fg;
	ag=bg=cg=dg=eg=fg=0 ;
	if (a) ag=a->glyph();
	if (b) bg=b->glyph();
	if (c) cg=c->glyph();
	if (d) dg=d->glyph();
	if (e) eg=e->glyph();
	if (f) fg=f->glyph();
	return is_room_for(ag,bg,cg,dg,eg,fg);
}

Coord VerticalPosition::width() const
{
	return graph().width();
}

int VerticalPosition::is_room_for(Glyph * a, Glyph * b, Glyph * c, Glyph * d,
	 Glyph * e, Glyph * f)
{
	// LogOut << "VerticalPosition::is_room_for, width = " << width() << "\n" ;
	Coord reserved_space = 20. ;
	// LogOut << "reserved_space = " << reserved_space << "\n" ;
	Coord total = reserved_space ;
	for (GlyphIndex ix = 0 ; ix < the_hbox->count(); ix++)
        total += DspApplication::x_req(the_hbox->component(ix));
	// return (reserved_space + DspApplication::x_req(the_hbox) +
	return (total +
		DspApplication::x_req(a) +
		DspApplication::x_req(b) + DspApplication::x_req(c) +
		DspApplication::x_req(d) + DspApplication::x_req(e) +
		DspApplication::x_req(f)
		) <= width() ;
}

EdibleGlyph * VerticalPosition::select_home()
{
	the_glyphs.next(0);
	HorizontalPosition * pos ;
	while (pos = the_glyphs.next()) {
		VerticalGlyphs::Flavor flavor = pos->flavor();
		if (flavor == VerticalGlyphs::node_ ||
			flavor == VerticalGlyphs::connector_)
				return pos->cookie()->select();
	}
	return 0 ;
}

EdibleGlyph * VerticalPosition::select_end()
{
	HorizontalPosition * pos ;
	for (GlyphIndex i = the_glyphs.number() -1; i > -1; i--) {
		pos = the_glyphs.get(i);
		VerticalGlyphs::Flavor flavor = pos->flavor();
		if (flavor == VerticalGlyphs::node_ ||
			flavor == VerticalGlyphs::connector_)
				return pos->cookie()->select();
	}
	return 0 ;
}

Coord VerticalPosition::middle_location(HorizontalPosition& pos)
{
	Coord middle = 0.0 ;
	float fact = 1. ;
	for (GlyphIndex i = 0; i <= pos.index(); i++) {
		if (i == pos.index()) fact = .5 ;
		middle += fact * DspApplication::x_req(the_glyphs.get(i)->glyph());
		// LogOut << "middle[" << i << "] = " << middle << "\n" ;
	}
	// LogOut << "middle = " << middle << "\n" ;
	return middle ;
}

EdibleGlyph * VerticalPosition::select_location(Coord where)
{
	Coord end = 0.0 ;
	HorizontalPosition * pos = 0 ;
	EdibleGlyph * last_selectable = 0 ;
	for (GlyphIndex i = 0; i < number(); i++) {
		end += DspApplication::x_req((pos = position(i))->glyph());
		// LogOut << "end[" << i << "] = " << end << "\n" ;
		EdibleGlyph * g = pos->cookie();
		if (end >= where) {
			if (g->select()) return g ;
			if (last_selectable) return last_selectable->select();
		}
		if (g->selectable()) last_selectable = g ;
	}
	if (!last_selectable) return 0 ;
	EdibleGlyph * ret = last_selectable->select(); ;
	if (ret) the_vertical_glyphs.make_visible(the_index);
	return ret ;
}

EdibleGlyph * VerticalPosition::select_up(HorizontalPosition& pos)
{
	// LogOut << "VerticalPosition::select_up\n" ;
	if (the_index < 1) return 0 ;
	Coord the_location = middle_location(pos);
	EdibleGlyph * new_selection = 0 ;
	for (GlyphIndex to_check = the_index - 1 ; to_check > -1 ; to_check--) {
		VerticalPosition * above = the_vertical_glyphs.get(to_check);
		if (!above) continue ;
		new_selection = above->select_location(the_location);
		if (new_selection) break ;
	}
	return new_selection ;
}

EdibleGlyph * VerticalPosition::select_down(HorizontalPosition& pos)
{
	// LogOut << "VerticalPosition::select_down\n" ;
	GlyphIndex limit = the_vertical_glyphs.number()  ;
	if (the_index >  limit -  2 ) return 0 ;
	Coord the_location = middle_location(pos);
	EdibleGlyph * new_selection = 0 ;
	for (GlyphIndex to_check = the_index + 1; to_check < limit ; to_check++) {
		VerticalPosition * below = the_vertical_glyphs.get(to_check);
		if (!below) continue ;
		new_selection = below->select_location(the_location);
		if (new_selection) break ;
	}
	return new_selection ;

}

EdibleSpace::EdibleSpace(Coord natural):
	MonoGlyph(DspApplication::layout().hglue(natural)),
	EdibleGlyph(VerticalGlyphs::space_)
{
}

EdibleGlyph::EdibleGlyph(VerticalGlyphs::Flavor flavor):
	the_flavor(flavor),
    the_horizontal_position(*(new HorizontalPosition(this))),
	the_vertical_position(0)
{
}

EdibleGlyph::~EdibleGlyph()
{
	delete (HorizontalPosition *) &the_horizontal_position ;
}

void EdibleGlyph::draw_connect(Canvas* c, const Allocation& a) const
{
	EdibleGlyph * This = (EdibleGlyph *) this ;
	switch(the_flavor) {
case VerticalGlyphs::node_:
		This->node()->draw_connect_all(c,a);
		break ;
case VerticalGlyphs::connector_:
		This->connector()->draw_connect_all(c,a);
		break ;
case VerticalGlyphs::input_label_:
case VerticalGlyphs::output_label_:
case VerticalGlyphs::space_:
		break ;
default:
		DbgError("EdibleGlyph::draw_connect","bad switch");
	}
}

void EdibleGlyph::remove_me()
{
	// LogOut << "EdibleGlyph::remove_me()\n" ;
	if (!the_vertical_position) return ;
	the_horizontal_position.remove_from(the_vertical_position);
	the_vertical_position = 0 ;
}

void EdibleGlyph::base_enter()
{
	NetGraph * gr = get_graph();
	if (gr) {
		if (selectable()) gr->set_mouse_selection(this);
		else gr->set_label_selection(this);
		gr->enter();
	}
}

void EdibleGlyph::base_leave()
{
	NetGraph * gr = get_graph();
	if (gr) {
		gr->clear_mouse_selection();
		gr->leave();
	}
}

Glyph * EdibleGlyph::glyph()
{
	DbgError("EdibleGlyph::glyph","base called");
	return 0 ;
}

NetGraph& EdibleGlyph::graph()
{
	NetGraph * gr = get_graph();
	if (!gr) DbgError("EdibleGlyph::graph","no graph");
	return *gr ;
}

NetGraph * EdibleGlyph::get_graph()
{
	if (!the_vertical_position) return 0 ;
	return &(the_vertical_position->graph());
}

NodeConnector * EdibleGlyph::connector()
{
	if (the_flavor != VerticalGlyphs::connector_)
		DbgError("EdibleGlyph::connector","tastes bad");
	return (NodeConnector *) glyph();
}

NodeGlyph * EdibleGlyph::node()
{
	if (the_flavor != VerticalGlyphs::node_)
		DbgError("EdibleGlyph::node","tastes bad");
	return (NodeGlyph *) glyph();
}

EdibleInputLabel * EdibleGlyph::input_label()
{
	if (the_flavor != VerticalGlyphs::input_label_)
		DbgError("EdibleGlyph::input_label","tastes bad");
	return (EdibleInputLabel *) glyph();
}

EdibleOutputLabel * EdibleGlyph::output_label()
{
	if (the_flavor != VerticalGlyphs::output_label_)
		DbgError("EdibleGlyph::output_label","tastes bad");
	return (EdibleOutputLabel *) glyph();
}

void EdibleGlyph::modified()
{
	if (!the_vertical_position) return ;
	GlyphIndex index = the_horizontal_position.index();
	// LogOut << "EdibleGlyph::modified, index = " << index << "\n" ;
	if (index < 0) return ;
	PolyGlyph* hbox = the_vertical_position->hbox();
	Glyph * gl = hbox->component(index);
	Resource::ref(gl);
	hbox->replace(index,gl);
	Resource::unref(gl);
}

void EdibleGlyph::unselect()
{
	if (the_flavor == VerticalGlyphs::node_) node()->unselect();
	if (the_flavor == VerticalGlyphs::connector_) connector()->unselect();
}

void EdibleGlyph::show_inputs()
{
/*
 *	LogOut << "EdibleGlyph::show_inputs, the_flavor = " << the_flavor
 *		<< "\n" ;
 */
	if (the_flavor == VerticalGlyphs::node_) {
		// LogOut << "Node name is `" << node()->name() << "'.\n" ;
		node()->show_inputs();
	}
	if (the_flavor == VerticalGlyphs::connector_)
		connector()->show_inputs();

	if (the_flavor == VerticalGlyphs::input_label_)
		input_label()->show_inputs();

	if (the_flavor == VerticalGlyphs::output_label_)
		output_label()->show_inputs();
}

void EdibleGlyph::show_outputs()
{
	if (the_flavor == VerticalGlyphs::node_) node()->show_outputs();
	if (the_flavor == VerticalGlyphs::connector_)
		connector()->show_outputs();
	if (the_flavor == VerticalGlyphs::input_label_)
		input_label()->show_outputs();
	if (the_flavor == VerticalGlyphs::output_label_)
		output_label()->show_outputs();
}

void EdibleGlyph::show_none()
{
	if (the_flavor == VerticalGlyphs::node_) node()->show_none();
	if (the_flavor == VerticalGlyphs::connector_)
		connector()->show_none();
	if (the_flavor == VerticalGlyphs::input_label_) input_label()->show_none();
	if (the_flavor == VerticalGlyphs::output_label_)
		output_label()->show_none();
}

int EdibleGlyph::selectable() const
{
	if (the_flavor == VerticalGlyphs::node_) return 1 ;
	if (the_flavor == VerticalGlyphs::connector_) return 1 ;
	return  0 ;
}

EdibleGlyph * EdibleGlyph::select()
{
	if (the_flavor == VerticalGlyphs::node_) return node()->select();
	if (the_flavor == VerticalGlyphs::connector_) return connector()->select();
	return 0 ;
}

EdibleGlyph * EdibleGlyph::select_left()
{
	EdibleGlyph * left = the_horizontal_position.select_left();
	if (left) return left ;
	return 0 ;
}

EdibleGlyph * EdibleGlyph::select_right()
{
	EdibleGlyph * right = the_horizontal_position.select_right();
	if (right) return right ;
	return 0 ;
}

EdibleGlyph * EdibleGlyph::select_up()
{
	if (the_vertical_position) {
		EdibleGlyph * up = the_vertical_position->select_up(
			the_horizontal_position);
		if (up) return up ;
	}
	return 0 ;
}

EdibleGlyph * EdibleGlyph::select_down()
{
	if (the_vertical_position) {
		EdibleGlyph * down = the_vertical_position->select_down(
			the_horizontal_position);
		if (down) return down ;
	}
	return 0 ;
}

Glyph * HorizontalPosition::glyph()
{
	return the_cookie->glyph();
}

void HorizontalPosition::remove_from(VerticalPosition * from)
{
	// LogOut<<"HorizontalPosition::remove_from, index = " << the_index<<"\n" ;
	if (!from) return ;
	if (the_index < 0) return ;
	from->Remove(the_index);
	the_index = -1 ;
}

EdibleGlyph * HorizontalPosition::select_left()
{
	VerticalPosition * where = the_cookie->vertical_position();
	for (GlyphIndex i = the_index - 1  ; i > -1; i--) {
		HorizontalPosition * pos = where->position(i);
		EdibleGlyph * sel = pos->cookie()->select();
		if (sel) return sel ;
	}
	return 0 ;
}

EdibleGlyph * HorizontalPosition::select_right()
{
	VerticalPosition * where = the_cookie->vertical_position();
	for (GlyphIndex i = the_index + 1; i < where->size() ; i++) {
		HorizontalPosition * pos = where->position(i);
		EdibleGlyph * sel = pos->cookie()->select();
		if (sel) return sel ;
	}
	return 0 ;
}

EdibleInputLabel::EdibleInputLabel(NetGraph& graph, EditLink& link):
	EdibleGlyph(VerticalGlyphs::input_label_),
		DspActiveHandler(0,graph.style()),
		the_node(&(link.glyph.node())),
		the_next_input(0),
		text(0),
		patch(0),
		the_channel(link.channel())
{
	new_node_kernel();
	make_body(graph.layout());
	the_node->next_input(this);
}

EdibleInputLabel::EdibleInputLabel(TraverseObj& obj, NodeDescription& node,
	int node_chan):
		EdibleGlyph(VerticalGlyphs::input_label_),
		DspActiveHandler(0,obj.style()),
		the_node(&node),
		the_next_input(0),
		text(0),
		patch(0),
		the_channel(node_chan)
{
	new_node_kernel();
	make_body(obj.layout());
	the_node->next_input(this);
}

void EdibleInputLabel::new_node()
{
	new_node_kernel();
	make_body(the_node->node_glyph()->graph().layout());
	modified();
}

void EdibleInputLabel::new_node_kernel()
{
	make_label();
	color_label(DspApplication::black());
}

void EdibleInputLabel::make_body(LayoutKit& layout)
{
	body(
		layout.vbox(
			layout.vglue(NodeGlyph::vmargin_size),
			&patch,
			layout.vglue(NodeGlyph::vmargin_size)
		)
	);
}

void EdibleInputLabel::make_label()
{
	delete text ;
	const char * node_name = the_node->name();
/*
 *	if (the_channel < 0) LogOut << "EdibleInputLabel::EdibleInputLabel" <<
 *		" negative channel for `" << node_name<<"' = " << the_channel << "\n" ;
 */
	if (the_channel) 
		text = Concatenate(DspApplication::int_string(the_channel)," ",
			node_name);
	else text = Concatenate(node_name);
}

void EdibleInputLabel::color_label(const Color *color)
{
	Label * label = new Label(text,DspApplication::font(),color);
	patch.body(label);
}

void EdibleInputLabel::color_input_labels(const Color * color)
{
	// LogOut << "coloring `" << text << "' to " << (void *) color << ".\n" ;
	color_label(color);
	patch.redraw();
	if (the_next_input) the_next_input->color_input_labels(color);
}


NodeGlyph& EdibleInputLabel::node_glyph()
{
	NodeGlyph * ret = the_node->node_glyph();
	if (!ret) DbgError("EdibleInputLabel::node_glyph","no glyph");
	return *ret ;
}

void EdibleInputLabel::mark_inputs_used()
{
	the_node->node_glyph()->mark_input(the_channel);
	if (the_next_input) the_next_input->mark_inputs_used();
}

void EdibleInputLabel::new_node(NodeDescription& nd)
{
	if (the_next_input) the_next_input->new_node(nd);
	the_node = &nd ;
	new_node();
}

EdibleOutputLabel::EdibleOutputLabel(NetGraph& graph, EditLink& link,
	int buffer_chan):
	EdibleGlyph(VerticalGlyphs::output_label_),
		DspActiveHandler(0,graph.style()),
		the_node(&(link.glyph.node())),
		the_next_output(0),
		text(0),
		patch(0),
		the_channel(link.channel()),
		the_displayed_buffer(0),
		the_buffer_channel(buffer_chan)
{
	new_node_kernel();
	make_body(graph.layout(),graph.kit());
	the_node->next_output(this);
}

EdibleOutputLabel::EdibleOutputLabel(TraverseObj& obj,
	NodeDescription& node):
		EdibleGlyph(VerticalGlyphs::output_label_),
		DspActiveHandler(0,obj.style()),
		the_node(&node),
		the_next_output(0),
		text(0),
		patch(0),
		the_channel(node.display_parameters()->channel()),
		the_displayed_buffer(0),
		the_buffer_channel(node.display_parameters()->buffer_channel())
{
	// LogOut << "EdibleOutputLabel::ctr\n" ;
	new_node_kernel();
	make_body(obj.layout(),obj.kit());
	the_node->next_output(this);
}

void EdibleOutputLabel::new_node()
{
	new_node_kernel();
	NetGraph& graph = the_node->node_glyph()->graph();
	make_body(graph.layout(),graph.kit());
	modified();
}

void EdibleOutputLabel::new_node_kernel()
{
	make_label();
	color_label(DspApplication::black());
}

void EdibleOutputLabel::make_label()
{
	const char * node_name = the_node->name();
/*
 *	LogOut << "node_name = `" << node_name << "', channel = " << the_channel
 *		<< ", buffer_channel = " << the_buffer_channel << ".\n" ;
 */
	char * temp = 0 ;
	if (the_channel && the_buffer_channel) {
		char * chan_string =  Concatenate(
			DspApplication::int_string(the_channel));
		text = Concatenate(node_name," ",chan_string,
			" B",DspApplication::int_string(the_buffer_channel));
		delete chan_string ;
	}
	else if (the_channel)
		text = Concatenate(node_name,
			" ",DspApplication::int_string(the_channel));
	else if (the_buffer_channel)
		text = Concatenate(node_name,
			" B",DspApplication::int_string(the_buffer_channel));
	else text = Concatenate(node_name);
}

void EdibleOutputLabel::vertical_position(VerticalPosition * vertical)
{
	EdibleGlyph::vertical_position(vertical);
	if (the_displayed_buffer) the_displayed_buffer->vertical_position(vertical);
}

void EdibleOutputLabel::make_body(LayoutKit& layout,WidgetKit& kit)
{

	BufferDescription* buffer = the_node->buffer(the_channel);
	
	int buffer_widget_out = 0 ;
	NodeGlyph * node_glyph = the_node->node_glyph();
	if (node_glyph) if (the_channel == node_glyph->glyph_input_connect() &&
		the_buffer_channel == node_glyph->glyph_buffer_connect())
		buffer_widget_out = 1 ;
	Glyph * connector_glyph ;
	if (!the_buffer_channel && buffer->out() && !buffer_widget_out) {
		NodeConnector * conn = new NodeConnector(*this);
		displayed_buffer(conn);
		connector_glyph = conn ;
	} else connector_glyph =
		new ArrowWidget(kit,layout,ArrowWidget::right_arrow) ;

	body(
		layout.hbox(
			layout.vbox(
				layout.vglue(NodeGlyph::vmargin_size),
				&patch,
				layout.vglue(NodeGlyph::vmargin_size)
			),
			connector_glyph 
		)
	);
}

void EdibleOutputLabel::color_label(const Color *color)
{
	Label * label = new Label(text,DspApplication::font(),color);
	patch.body(label);
}

void EdibleOutputLabel::color_output_labels(const Color * color)
{
	// LogOut << "coloring `" << text << "' to " << (void *) color << ".\n" ;
	color_label(color);
	patch.redraw();
	if (the_next_output) the_next_output->color_output_labels(color);
}

NodeGlyph& EdibleOutputLabel::node_glyph()
{
	NodeGlyph * ret = the_node->node_glyph();
	if (!ret) DbgError("EdibleOutputLabel::node_glyph","no glyph");
	return *ret ;
}

void EdibleOutputLabel::mark_outputs_used()
{
	// LogOut << "`" << text << "' " << the_channel << "\n" ;
	the_node->node_glyph()->mark_output(the_channel);
	if (the_next_output) the_next_output->mark_outputs_used();
}


void EdibleOutputLabel::new_node(NodeDescription& nd)
{
	if (the_next_output) the_next_output->new_node(nd);
	the_node = &nd ;
	new_node();
}

NodeConnector * EdibleOutputLabel::get_node_connector(int channel)
{
/*
 *	LogOut << "EdibleOutputLabel::get_node_connector(" << channel <<
 *		", text = `" << text << "'\n" ;
 */
	if (the_channel == channel) if (!the_buffer_channel) {
		// LogOut << "returning displayed_buffer()\n" ;
		return displayed_buffer();
	}
	if (the_next_output) return the_next_output->get_node_connector(channel);
	return 0 ;
}

void EditLink::set_channel()
{
	switch(type()) {
default:
case EditConnection::connect_replace:
case EditConnection::connect_bad:
		DbgError("EditLink::set_channel","bad type");
case EditConnection::connect_input:
		glyph.set_input_channel(link.channel);
		break ;
case EditConnection::connect_output:
case EditConnection::connect_buffer:
		glyph.set_output_channel(link.channel);
		break ;
	}

}

int EditLink::in_network()
{
	return glyph.in_network();
}

LastPosition::LastPosition():left(0),right(0),top(0),bottom(0)
{
}

void LastPosition::set(const Allocation& a)
{
	left=a.left();
	right=a.right();
	top=a.top();
	bottom=a.bottom();
}

Coord LastPosition::center_x() const
{
	return .5 *(left+right);
}

Coord LastPosition::center_y() const 
{
	return .5 * (top+bottom);
}

