/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
/****************************************************************
 * File: page.c
 * Date: 05/06/91
 *
 * Description:
 *   This file contains the core functions for rendering an VNS
 *   page. Several other functions are contained in page2.c and
 *   page_header.c. It all use to be in one file but had to be
 *   splitt to get it to compile on the RS6000.
 *
 * Revisions:
 ****************************************************************/
#include <stdio.h>
#include <Xm/Xm.h>
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/CascadeB.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/ArrowB.h>
#include <Xm/ToggleBG.h>
#include <Xm/ScrolledW.h>
#include <string.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <VList.h>
#include <VnsBackground.h>
#include <VnsText.h>

#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include <X11/Xatom.h>
#include <X11/CoreP.h>
#include <X11/Shell.h>

#include "VnsP.h"
#include "props.h"

/* externa bitmaps (create_bmaps.c) */
extern Pixmap page_bitmap;
extern Pixmap dlink_bitmap;

/* turn the grid on or off for all displayed pages */
VnsSetGrid(vns,state)
	VnsContext *vns;
	Boolean state;
{
	VnsPage *pl = vns->first_page;

	vns->session->show_grid = state;

	while (pl != NULL)
	{
		XtVaSetValues(pl->comp,
			XtNshowGrid,state,
			NULL);
		pl = pl->npage;
	}
}

/* turn object outlines on or off for all displayed pages */
VnsSetOutlines(vns,state)
	VnsContext *vns;
	Boolean state;
{
	VnsPage *pl = vns->first_page;

	vns->session->show_outlines = state;

	while (pl != NULL)
	{
		XtVaSetValues(pl->comp,
			XtNshowOutlines,state,
			NULL);
		pl = pl->npage;
	}
}

/* turn snap to grid on or off for all displayed pages */
VnsSetSnapToGrid(vns,state)
	VnsContext *vns;
	Boolean state;
{
	VnsPage *pl = vns->first_page;

	vns->session->snap_to_grid = state;

	while (pl != NULL)
	{
		XtVaSetValues(pl->comp,
			XtNsnapToGrid,state,
			NULL);
		pl = pl->npage;
	}
}

VnsSetAutoPageOpen(vns,state)
	VnsContext *vns;
	Boolean state;
{
	vns->session->auto_page_open = state;
}

/*
 * This function copies selected objects and places them into the
 * paste buffer.
 */
copy_objects(pl)
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsObject *obj;

	/* return if no objects are selected on a page */
	if (pl->nselected <= 0) return;

	/* if the paste buffer contains objects, free them */
	if (vns->clip_board != NULL)
	{
		VnsPaste *p = vns->clip_board;
		VnsPaste *tmp;
		while(p != NULL)
		{
			switch(p->type)
			{
				case OT_TXT:
					free(p->data.text.txt);
					break;
				case OT_IMAGE:
					destroy_img(&p->data.image.img);
					break;
			}
			tmp = p;
			p = p->next;
			free((char *)tmp);
		}

		/*
		 * at this point, we deactivate the undo menu item since the
		 * buffer has changed. 
		 */ 
		XtSetSensitive(pl->w_undo,False);
	}

	vns->clip_board = NULL;

	/* search through list of objects and add selected objects to paste buffer */
	obj = pl->first_object;
	while(obj != NULL)
	{
		if (obj->selected)
		{
			VnsPaste *p = (VnsPaste *)malloc(sizeof(VnsPaste));
			p->type = obj->info.type;
			p->width = obj->info.width;
			p->height = obj->info.height;
			p->x = obj->info.x;
			p->y = obj->info.y;
			switch(p->type)
			{
				case OT_TXT:
				{
					char *s;
					XtVaGetValues(obj->auxil.txt.wd,
						XtNstring,&s,
						NULL);
					p->data.text.txt = strdup(s);
					p->data.text.info = obj->info.data.t_info;
					break;
				}
				case OT_IMAGE:
				{
					p->data.image.info = obj->info.data.i_info;
					copy_img(&obj->auxil.img.img,&p->data.image.img);
					break;
				}
				case OT_LINK:
					p->data.link = obj->info.data.l_info;
					break;
				case OT_ACTION_LINK:
					p->data.alink = obj->info.data.al_info;
			}

			/* add the object to the clip board */
			p->next = vns->clip_board;
			vns->clip_board = p;
		}
		obj = obj->next;
	}
}

/*
 * utility function for copying an image
 */
copy_img(img1,img2)
	Rast_Img *img1;
	Rast_Img *img2;
{
	int size = (((img1->width * img1->depth + 15) / 16) * 2) * img1->height;
	img2->height = img1->height;
	img2->width = img1->width;
	img2->depth = img1->depth;
	img2->data = (unsigned char *)malloc((unsigned)size);
	img2->cmap_type = img1->cmap_type;
	bcopy((char *)img1->data,(char *)img2->data,size);
	if (img1->cmap_type == CMAP_RGB)
	{
		int size = img1->cmap_data.rgb.map_size * sizeof(struct rgb);
		img2->cmap_data.rgb.map_size = img1->cmap_data.rgb.map_size;
		img2->cmap_data.rgb.map = (struct rgb *)malloc((unsigned)size);
		bcopy((char *)img1->cmap_data.rgb.map,(char *)img2->cmap_data.rgb.map,size);
	}
}

/*
 * destroy a raster image and free memory.
 */
destroy_img(img)
	Rast_Img *img;
{
	free((char *)img->data);
	if (img->cmap_type == CMAP_RGB)
	{
		free((char *)img->cmap_data.rgb.map);
	}
}


/*
 * This function deletes all visible reference link objects
 * to the page specified.
 */
update_page_refs(pl)
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	VnsPage *pl2 = vns->first_page;

	/* Look for any links to this page */
	while(pl2 != NULL)
	{
		VnsObject **ol = &(pl2->first_object);
		struct return_page **rtrn = &(pl2->retrn);
		while(*ol != NULL)
		{
			if ((*ol)->info.type == OT_LINK &&
			    (*ol)->info.data.l_info.dest_page == pl->pid &&
				(*ol)->info.data.l_info.dest_note == nl->nid)
			{
				/* reset the link to dangling mode */
				(*ol)->info.data.l_info.dest_note = -1;
				(*ol)->info.data.l_info.dest_page = -1;
				/* change the button bitmap */
				XtVaSetValues((*ol)->auxil.link.button,
					XmNlabelPixmap,dlink_bitmap,
					NULL);
			}
			else
			{
				ol = &(*ol)->next;
			}
		}
		while(*rtrn != NULL)
		{
			if ((*rtrn)->pid.nl->nid == nl->nid &&
			    (*rtrn)->pid.pid == pl->pid)
			{
				(*rtrn)->count--;
				if (!(*rtrn)->count)
				{
					struct return_page *old = *rtrn;
					*rtrn = old->next;
					free((char *)old);
					continue;
				}
			}
			rtrn = &(*rtrn)->next;
		}
		pl2 = pl2->npage;
	}
}

/*
 * This function only deletes links to a spcified page
 * in a specified notebook.
 */
delete_links_to(nl,pid)
	VnsNotebook *nl;
	int pid;
{
	VnsContext *vns = NoteToVns(nl);
	VnsPage *pl = vns->first_page;
	int nid;

	nid = nl->nid;
	while(pl != NULL)
	{
		VnsObject **ol = &(pl->first_object);
		while(*ol != NULL)
		{
			if ((*ol)->info.type == OT_LINK &&
			    (*ol)->info.data.l_info.dest_note == nid &&
				(*ol)->info.data.l_info.dest_page == pid)
			{
				del_object(*ol);
			}
			else
			{
				ol = &(*ol)->next;
			}
		}
		pl = pl->npage;
	}
}

/*
 * This function deletes all visible reference link objects
 * to the page specified.
 */
delete_page_refs(pl)
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	VnsPage *pl2 = vns->first_page;

	/* Look for any links to this page */
	while(pl2 != NULL)
	{
		VnsObject **ol = &(pl2->first_object);
		struct return_page **rtrn = &(pl2->retrn);
		while(*ol != NULL)
		{
			if ((*ol)->info.type == OT_LINK &&
			    (*ol)->info.data.l_info.dest_page == pl->pid &&
				(*ol)->info.data.l_info.dest_note == nl->nid)
			{
				del_object(*ol);
			}
			else
			{
				ol = &(*ol)->next;
			}
		}
		while(*rtrn != NULL)
		{
			if ((*rtrn)->pid.nl->nid == nl->nid &&
			    (*rtrn)->pid.pid == pl->pid)
			{
				(*rtrn)->count--;
				if (!(*rtrn)->count)
				{
					struct return_page *old = *rtrn;
					*rtrn = old->next;
					free((char *)old);
					continue;
				}
			}
			rtrn = &(*rtrn)->next;
		}
		pl2 = pl2->npage;
	}
}

/* Deletes page from notebook */
del_page(pl)
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	VnsPage **p = VnsFindPage(nl,pl->pid);

	/*
	 * propagate stacking page to the return page if active.
	 */
	if (pl == vns->stacking_page)
	{
		if (pl->retrn != NULL)
		{
			VnsPage *last = *(VnsFindPage(pl->retrn->pid.nl,pl->retrn->pid.pid));

			if (last != NULL)
			{
				/* activate the stack toggle on return page */
				XtVaSetValues(last->stack,
					XmNset,True,
					NULL);

				/* set the stacking page to the return page */
				vns->stacking_page = last;
			}
		}
	}

	/*
	 * set the tunnel page to NULL if we are deleting the tunnel page
	 */
	if (pl == vns->tunnel_page)
	{
		vns->tunnel_page = NULL;
	}

	if(*p != NULL)
	{
		VnsPage **plp = &vns->first_page;
		while(*plp != NULL)
		{
			if (*plp == pl)
			{
				break;
			}
			plp = &((*plp)->npage);
		}

		/* reset the property manager (could be a callback!) */
		VnsPropResetPage(pl);

		VtUnmanageChildren(pl->comp);
		while(pl->first_object != NULL)
		{
			del_object(pl->first_object);
		}
		{
			struct return_page *pg = pl->retrn;
			while(pg != NULL && !--(pg->count))
			{
				struct return_page *nxt = pg->next;
				free((char *)pg);
				pg = nxt;
			}
		}

		/* destroy the pixmaps */
		XmDestroyPixmap(XtScreen(pl->pwd),pl->exit_pixmap);
		XmDestroyPixmap(XtScreen(pl->pwd),pl->home_pixmap);
		XmDestroyPixmap(XtScreen(pl->pwd),pl->link_here_pixmap);
		XmDestroyPixmap(XtScreen(pl->pwd),pl->link_here_pixmap_s);
		XmDestroyPixmap(XtScreen(pl->pwd),pl->return_pixmap);
		XmDestroyPixmap(XtScreen(pl->pwd),pl->return_pixmap_s);
		XmDestroyPixmap(XtScreen(pl->pwd),pl->tunnel_pixmap);
		XmDestroyPixmap(XtScreen(pl->pwd),pl->tunnel_pixmap_s);
		XmDestroyPixmap(XtScreen(pl->pwd),pl->stack_pixmap);
		XmDestroyPixmap(XtScreen(pl->pwd),pl->stack_pixmap_s);

		/* destory the toplevel page shell */
		XtDestroyWidget(pl->pwd);

		*p = pl->next;
		if (*plp != NULL)
		{
			*plp = pl->npage;
		}
		free((char *)pl);
	}
}

set_page_colormaps(pl)
	VnsPage *pl;
{
	Display *dpy = XtDisplay(pl->comp);
	Colormap def = DefaultColormap(dpy,DefaultScreen(dpy));
	VnsObject *o;
	Widget *children;
	int n = 0;

	for(o = pl->first_object; o != NULL; o = o->next)
	{
		if (o->info.type == OT_IMAGE &&
		    o->auxil.img.wd->core.colormap != def)
		{
			n++;
		}
	}
	children = (Widget *)malloc((unsigned)((n + 1) * sizeof(Widget)));
	children[0] = pl->pwd;
	for(n = 1,o = pl->first_object; o != NULL; o = o->next)
	{
		if (o->info.type == OT_IMAGE &&
		    o->auxil.img.wd->core.colormap != def)
		{
			children[n++] = o->auxil.img.wd;
		}
	}
	XtSetWMColormapWindows(pl->pwd,children,(Cardinal)n);
	free((char *)children);
}

set_page_accesses(new)
	VnsPage *new;
{
	VnsNotebook *nl = new->parent;
	VnsObject *ol;
	char buf[512];

	XtVaSetValues(new->w_create_menu,
		XmNsensitive,nl->info.can_write,
		NULL);
	XtVaSetValues(new->w_create_page,
		XmNsensitive,nl->info.can_write,
		NULL);

	XtVaSetValues(new->w_paste,
		XmNsensitive,nl->info.can_write,
		NULL);

	XtVaSetValues(new->w_x_paste,
		XmNsensitive,nl->info.can_write,
		NULL);
	XtVaSetValues(new->w_object_del,
		XmNsensitive,nl->info.can_write,
		NULL);

	/* set access for selected text objects */
	ol = new->first_object;
	while(ol != NULL)
	{
		if (ol->info.type == OT_TXT)
		{
			XtVaSetValues(ol->auxil.txt.wd,
				XtNreadOnly,nl->info.can_write,
				NULL);
		}
		ol = ol->next;
	}

	/* set the page title accordingly */
	if (nl->info.can_write)
	{
		XtVaSetValues(new->pwd,
			XmNtitle,new->info.title,
			NULL);
	}
	else
	{
		sprintf(buf,"%s [Read Only]",new->info.title);
		XtVaSetValues(new->pwd,
			XmNtitle,buf,
			NULL);
	}
}

static
img_callback(pl,image)
	VnsPage *pl;
	XImage *image;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	VnsObject *obj;
	Object_Block block;
	int x,y;

	vns_watch_on(vns);

	VtGetImgFromImage(vns->dpy,image,&block.img);
	XDestroyImage(image);

	/* obtain the current location of the cross hairs on the page background */
	XtVaGetValues(pl->comp,
		XtNlocX,&x,
		XtNlocY,&y,
		NULL);

	/* fill in block information for image object */
	block.info.x = x;
	block.info.y = y;
	block.info.width = block.img.width;
	block.info.height = block.img.height;
	block.info.type = OT_IMAGE;
	block.info.data.i_info.low_res_sw = 0;
	block.info.data.i_info.create_cmap = 0;

	/* create the image object in the database */
	block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);

	srv_set_object_img(PageToSrv(pl),nl->nid,pl->pid,block.oid,&block.img);
	obj = add_object2(pl,&block);
	select_object(obj);
	set_page_colormaps(pl);
	update_page_scrollbars(pl);
	vns_watch_off(vns);
}

/*ARGSUSED*/
void
sel_create_img(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	/* call Vt library function to sweep out an image */
	VtSelectScreenArea(pl->pwd,img_callback,(XtPointer)pl);
}

/*ARGSUSED*/
void
sel_create_text(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	Object_Info info;
	VnsObject *obj;
	int oid;
	int x,y;

	vns_watch_on(vns);

	/* find the current cross hair location on the page background */
	XtVaGetValues(pl->comp,
		XtNlocX,&x,
		XtNlocY,&y,
		NULL);

	/* fill in object information */
	info.type = OT_TXT;
	info.x = x;
	info.y = y;
	info.height = 100;
	info.width = 100;
	strcpy(info.data.t_info.font,vns->default_font);
	strcpy(info.data.t_info.color,"black");

	/* creaqe the object in the database */
	oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&info);

	/* create the object on the page */
	obj = add_object(pl,oid);

	/* select the new object */
	select_object(obj);

	update_page_scrollbars(pl);

	vns_watch_off(vns);
}

/*ARGSUSED*/
void
sel_create_page(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	VnsPage **ref, *hp;
	Page_Info page_info;
	int pid;

	vns_watch_on(vns);

	/* get the home page width and height for this notebook */
	ref = VnsFindPage(nl,nl->info.home_page);
	hp = *ref;
	if (hp != NULL)
	{
		page_info.width = hp->info.width;
		page_info.height = hp->info.height;
	}
	else
	{
		Page_Info home_info;
		srv_inq_page(PageToSrv(pl),nl->nid,nl->info.home_page,&home_info);
		page_info.width = home_info.width;
		page_info.height = home_info.height;
	}

	/* fill in page informations */
	strcpy(page_info.color,"white");

	/* create the new page in the database for the current notebook */
	pid = srv_create_page(PageToSrv(pl),nl->nid,&page_info);
	find_last_page_of_note(nl);

	/* update the page name */
	sprintf(page_info.title,"Page %d",pid);
	srv_modify_page(PageToSrv(pl),nl->nid,pid,&page_info);

	/* create link on the current page */
	{
		VnsObject *obj;
		XFontStruct *font;
		Object_Block block;
		int x,y;

		/* find the current cross hair location on the page background */
		XtVaGetValues(pl->comp,
			XtNlocX,&x,
			XtNlocY,&y,
			NULL);

		/* fill in information for link object */
		block.info.x = x;
		block.info.y = y;
		font = alloc_named_font(vns,vns->last_font);
		block.info.width = XTextWidth(font,page_info.title,strlen(page_info.title)) + 50;
		block.info.height = 20;
		block.info.type = OT_LINK;
		block.info.data.l_info.dest_note = nl->nid;
		block.info.data.l_info.dest_page = pid;
		strcpy(block.info.data.l_info.title,page_info.title);
		strcpy(block.info.data.l_info.color,"black");
		strcpy(block.info.data.l_info.font,vns->last_font);

		block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
		obj = add_object2(pl,&block);
		select_object(obj);
		update_page_scrollbars(pl);

		if (vns->session->auto_page_open)
		{
			VnsAddPage(nl,pid,(struct return_page *)NULL,(char *)NULL,
				(VnsPage *)NULL);
		}
	}
	vns_watch_off(vns);
}

/*ARGSUSED*/
void
sel_create_link(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	static char *title = "Untitled";
	VnsObject *obj;
	XFontStruct *font;
	Object_Info link_info;
	int x,y;
	int oid;

	vns_watch_on(vns);

	/* find the current cross hair location on the page background */
	XtVaGetValues(pl->comp,
		XtNlocX,&x,
		XtNlocY,&y,
		NULL);

	/* fill in information for link object */
	link_info.x = x;
	link_info.y = y;
	font = alloc_named_font(vns,vns->last_font);
	link_info.width = XTextWidth(font,title,strlen(title)) + 50;
	link_info.height = 20;
	link_info.type = OT_LINK;
	/* set destination as dangling link */
	link_info.data.l_info.dest_note = -1;
	link_info.data.l_info.dest_page = -1;
	strcpy(link_info.data.l_info.title,title);
	strcpy(link_info.data.l_info.color,vns->last_color);
	strcpy(link_info.data.l_info.font,vns->last_font);

	/* create the link in the database */
	oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&link_info);

	/* add the link object to the displayed page */
	obj = add_object(pl,oid);

	/* select the new link object */
	select_object(obj);

	update_page_scrollbars(pl);

	vns_watch_off(vns);
}

/*ARGSUSED*/
void
sel_create_alink(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	VnsObject *obj;
	XFontStruct *font;
	Object_Info info;
	int x,y;
	int oid;

	vns_watch_on(vns);

	/* find the current cross hair location on the page background */
	XtVaGetValues(pl->comp,
		XtNlocX,&x,
		XtNlocY,&y,
		NULL);

	/* fill in information for link object */
	info.x = x;
	info.y = y;
	font = alloc_named_font(vns,vns->last_font);
	info.width = XTextWidth(font,"Untitled",strlen("Untitled")) + 50;
	info.height = 20;
	info.type = OT_ACTION_LINK;
	strcpy(info.data.al_info.title,"Untitled");
	strcpy(info.data.al_info.color,vns->last_color);
	strcpy(info.data.al_info.font,vns->last_font);
	info.data.al_info.command[0] = '\0';

	/* create the new object in the database */
	oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&info);

	/* add the new object to the displayed page */
	obj = add_object(pl,oid);

	/* select the new object */
	select_object(obj);

	update_page_scrollbars(pl);

	vns_watch_off(vns);
}

/*
 * callback for undoing and editing function.
 */
/*ARGSUSED*/
void
sel_object_undo(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	VnsPaste *p = vns->clip_board;

	if (p == NULL) return;  /* nothing to undo */

	/*
	 * For any number of objects, we simply paste them back where
	 * they were cut from. We are assuming that a cut was the last
	 * operation performed. A more robust approach is required to 
	 * track more changes.
	 */
	while(p != NULL)
	{
		Object_Block block;

		block.info.x = p->x;
		block.info.y = p->y;
		block.info.width = p->width;
		block.info.height = p->height;
		block.info.type = p->type;
		switch(block.info.type)
		{
			case OT_TXT :
				block.info.data.t_info = p->data.text.info;
				block.txt = p->data.text.txt;
				block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
				srv_set_object_text(PageToSrv(pl),nl->nid,pl->pid,block.oid,
					block.txt);
				break;
			case OT_IMAGE :
				copy_img(&p->data.image.img,&block.img);
				block.info.data.i_info = p->data.image.info;
				block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
				srv_set_object_img(PageToSrv(pl),nl->nid,pl->pid,block.oid,
					&block.img);
				break;
			case OT_LINK :
				block.info.data.l_info = p->data.link;
				block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
				break;
			case OT_ACTION_LINK :
				block.info.data.al_info = p->data.alink;
				block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
				break;
		}
		{
			VnsObject *obj = add_object2(pl,&block);
			select_object(obj);
			update_page_scrollbars(pl);
		}
	
		switch(block.info.type)
		{
			case OT_IMAGE :
				set_page_colormaps(pl);
				break;
		}

		p = p->next;
	}

	/* now, make the undo button insensitive again */
	XtSetSensitive(pl->w_undo,False);
}

/*
 * Callback for Cut under the Edit menu
 */
/*ARGSUSED*/
void
sel_object_cut(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsNotebook *nl = PageToNote(pl);
	VnsObject **ol = &(pl->first_object);
	int w, h;

	if (pl->nselected <= 0)
	{
		VtMessage(pl->pwd,"No objects are currently selected.");
		return;
	}

	/* copy any selected objects to the paste buffer */
	copy_objects(pl);

	while(*ol != NULL)
	{
		if ((*ol)->selected)
		{
			int oid = (*ol)->oid;
			del_object(*ol);
			srv_delete_object(PageToSrv(pl),nl->nid,pl->pid,oid);

			XtSetSensitive(pl->w_undo,True);
		}
		else
		{
			ol = &(*ol)->next;
		}
	}

	/* update the scrollbars on the page after deleting objects */
	update_page_scrollbars(pl);
}

/*ARGSUSED*/
void
sel_object_paste(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	VnsPaste *p = vns->clip_board;

	if (p == NULL) return;  /* nothing to paste */

	/* 
	 * if only one object, then we place it at the current x,y coordinates.
	 */
	if (p->next == NULL)
	{
		int x,y;
		Object_Block block;

		/*
		 * for a single object, we paste the object where the cross hairs
		 * are current located on the VnsBackground widget.
		 */
		XtVaGetValues(pl->comp,
			XtNlocX,&x,
			XtNlocY,&y,
			NULL);

		block.info.x = x;
		block.info.y = y;
		block.info.width = p->width;
		block.info.height = p->height;
		block.info.type = p->type;
		switch(block.info.type)
		{
			case OT_TXT :
				block.info.data.t_info = p->data.text.info;
				block.txt = p->data.text.txt;
				block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
				srv_set_object_text(PageToSrv(pl),nl->nid,pl->pid,block.oid,
					block.txt);
				break;
			case OT_IMAGE :
				copy_img(&p->data.image.img,&block.img);
				block.info.data.i_info = p->data.image.info;
				block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
				srv_set_object_img(PageToSrv(pl),nl->nid,pl->pid,block.oid,
					&block.img);
				break;
			case OT_LINK :
				block.info.data.l_info = p->data.link;
				block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
				break;
			case OT_ACTION_LINK :
				block.info.data.al_info = p->data.alink;
				block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
				break;
		}
		{
			VnsObject *obj = add_object2(pl,&block);
			select_object(obj);
			update_page_scrollbars(pl);
		}
		switch(block.info.type)
		{
			case OT_IMAGE :
				set_page_colormaps(pl);
				break;
		}

		return;
	}
	else
	{
		/*
		 * For pasting multiple objects, we currently paste them at the original
		 * coordinates  from which they were cut or copied. This should probably
		 * be changed later so that a group of objects are place from an offset
		 * position defined by the VnsBackground cross hairs. But this cannot be
		 * implemented now becuase an approach handling cross hair location and
		 * activation with respect to the selection of other objects has not been
		 * developed.
		 */
		while(p != NULL)
		{
			Object_Block block;

			block.info.x = p->x;
			block.info.y = p->y;
			block.info.width = p->width;
			block.info.height = p->height;
			block.info.type = p->type;
			switch(block.info.type)
			{
				case OT_TXT :
					block.info.data.t_info = p->data.text.info;
					block.txt = p->data.text.txt;
					block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
					srv_set_object_text(PageToSrv(pl),nl->nid,pl->pid,block.oid,
						block.txt);
					break;
				case OT_IMAGE :
					copy_img(&p->data.image.img,&block.img);
					block.info.data.i_info = p->data.image.info;
					block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
					srv_set_object_img(PageToSrv(pl),nl->nid,pl->pid,block.oid,
						&block.img);
					break;
				case OT_LINK :
					block.info.data.l_info = p->data.link;
					block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
					break;
				case OT_ACTION_LINK :
					block.info.data.al_info = p->data.alink;
					block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
					break;
			}
			{
				VnsObject *obj = add_object2(pl,&block);
				select_object(obj);
				update_page_scrollbars(pl);
			}
	
			switch(block.info.type)
			{
				case OT_IMAGE :
					set_page_colormaps(pl);
					break;
			}

			p = p->next;
		}
	}

	/* restack the page objects after pasting */
	stack_objects(pl);
}

/*ARGSUSED*/
static
void
sel_del_win_page(w,pl)
	Widget w;
	VnsPage *pl;
{
	del_page(pl);
}

/*
 * Finds the first page in a list of pages.
 */
static
int
find_first_page(pages,npages)
	long *pages;
{
	int pid = pages[0];
	int i;
	for(i = 1; i < npages; i++)
	{
		if (pages[i] < pid)
		{
			pid = pages[i];
		}
	}
	return pid;
}

/*
 * Finds the last page in a list of pages.
 */
static
int
find_last_page(pages,npages)
	long *pages;
{
	int pid = pages[0];
	int i;
	if (npages <= 1)
	{
		return pid;
	}
	for(i = 1; i < npages; i++)
	{
		if (pages[i] > pid)
		{
			pid = pages[i];
		}
	}
	return pid;
}

/*
 * finds the next page in a list of pages.
 */
static
int
find_next_page(pages,npages,prev_id)
	long *pages;
{
	int pid = -1;
	int i;
	for(i = 0; i < npages; i++)
	{
		if (pages[i] > prev_id && (pid < 0 || pages[i] < pid))
		{
			pid = pages[i];
		}
	}
	return (pid < 0) ? find_first_page(pages,npages) : pid;
}

/*
 * Finds previous page in a list of pages
 */
static
int
find_prev_page(pages,npages,prev_id)
	long *pages;
{
	int pid = -1;
	int i;
	for(i = 0; i < npages; i++)
	{
		if (pages[i] < prev_id && (pid < 0 || pages[i] > pid))
		{
			pid = pages[i];
		}
	}
	return (pid < 0) ? find_last_page(pages,npages) : pid;
}

/*
 * Finds the last page for a notebook and then updates
 * the total number of pages in each notebook for each
 * page that is opened (displayed).
 */
find_last_page_of_note(note)
	VnsNotebook *note;
{
	VnsPage *pl = note->first_page;
	XmString xm_str;
	long *pages;
	long npages;
	int err;

	err = srv_inq_notebook_pages(NoteToSrv(note),note->nid,&pages,&npages);
	if (err < 0)
	{
		return;
	}
	if (pages)
	{
		note->last_page = find_last_page(pages,(int)npages);
	}
	else
	{
		note->last_page = npages;
	}
	while(pl != NULL)
	{
		char pagenum[100];

		sprintf(pagenum,"  Page %d of %d  ",pl->pid,note->last_page);
		xm_str = XmStringCreateLtoR(pagenum,XmSTRING_DEFAULT_CHARSET);
		XtVaSetValues(pl->w_pagenum,
			XmNlabelString,xm_str,
			NULL);
		XmStringFree(xm_str);
		pl = pl->next;
	}
}

VnsPage **
VnsFindPage(note,pid)
	VnsNotebook *note;
{
	VnsPage **p = &note->first_page;
	while(*p != NULL)
	{
		if ((*p)->pid == pid)
		{
			return p;
		}
		p = &((*p)->next);
	}
	return p;
}

/* Returns the color for a page given its name. */
static
unsigned long
get_page_color(vns,name)
	VnsContext *vns;
	char *name;
{
	XColor def,exact;

	if (XAllocNamedColor(vns->dpy,DefaultColormap(vns->dpy,DefaultScreen(vns->dpy)),
		name,&def,&exact))
	{
		return def.pixel;
	}
	return WhitePixelOfScreen(XtScreen(vns->toplevel));
}

VnsObject *
get_sel_object(pl)
	VnsPage *pl;
{
	VnsObject *obj;

	if (pl == NULL) return NULL;

	obj = pl->first_object;
	while(obj != NULL)
	{
		if (obj->selected)
		{
			return obj;
		}
		obj = obj->next;
	}

	return NULL;
}

/*
 * event handler for closing a page
 */
/*ARGSUSED*/
static
void
sel_close_page(wd,pl,cs)
	Widget wd;
	VnsPage *pl;
	XmAnyCallbackStruct *cs;
{
	VnsNotebook *nl = PageToNote(pl);

	/* if it is a shift-close, then close all pages in notebook */
	if (cs->event != NULL)
	{
		if (cs->event->xbutton.state & ShiftMask)
		{
			VnsPage *list = nl->first_page;
			while(list != NULL)
			{
				page_deselect(list);
				del_page(list);
				list = list->next;
			}
			return;
		}
	}

	/* this is the default operation for closing a page */
	page_deselect(pl);
	del_page(pl);
}

/* Callback to edit the page properties */
/*ARGSUSED*/
static
void
sel_page_props(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsPopupPropertyManager(PageToVns(pl));
	VnsPropPage(pl);
}

/* Callback when print is selected */
/*ARGSUSED*/
static
void
sel_page_print(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);

	/* deselect all objects before going to printing */
	deselect_all(vns);

	/* Activate the printing popup provided in the Vt library */
	VtPrint(pl->pwd,PageToSrv(pl),nl->nid,pl->pid,pl->info.title,
		vns->site_printer_file);
}

/* Callback when linke_here is pressed */
/*ARGSUSED*/
static
void
sel_link_here(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);

	if (vns->link_callback != NULL)
	{
		vns->link_callback(vns->link_data,nl->nid,pl->pid);
	}
}

/*ARGSUSED*/
static
void
sel_return_page(wd,pl,cs)
	Widget wd;
	VnsPage *pl;
	XmAnyCallbackStruct *cs;
{
	if (pl->retrn != NULL)
	{
		if (cs->event != NULL)
		{
			if (cs->event->xbutton.state & ShiftMask)
			{
				VnsAddPage(pl->retrn->pid.nl,pl->retrn->pid.pid,pl->retrn->next,
					(char *)NULL,pl);
				return;
			}
		}

		/* default action */
		VnsAddPage(pl->retrn->pid.nl,pl->retrn->pid.pid,pl->retrn->next,
			(char *)NULL,(VnsPage *)NULL);
	}
}

/*ARGSUSED*/
static
void
sel_home_page(wd,pl,cs)
	Widget wd;
	VnsPage *pl;
	XmAnyCallbackStruct *cs;
{
	VnsNotebook *nl = PageToNote(pl);

	int home_page = nl->info.home_page;
	if (srv_validate_page(PageToSrv(pl),nl->nid,home_page) == 1)
	{
		if (cs->event != NULL)
		{
			VnsAddPage(nl,home_page,add_page_to_path(pl),(char *)NULL,
				(cs->event->xbutton.state & ShiftMask) ? pl : (VnsPage *)NULL);
			return;
		}

		/* default action */
		VnsAddPage(nl,home_page,add_page_to_path(pl),(char *)NULL,
			(VnsPage *)NULL);
	}
	else
	{
            char msg[256];
            sprintf(msg,"Home Page of %s is not valid",nl->info.title);
            VtError(pl->pwd,msg);
	}
}

struct return_page *
add_page_to_path(pl)
	VnsPage *pl;
{
	struct return_page *new_path = (struct return_page *)malloc(sizeof *new_path);
	new_path->pid.pid = pl->pid;
	new_path->pid.nl = PageToNote(pl);
	new_path->next = pl->retrn;
	new_path->count = 0;
	if (pl->retrn != NULL)
	{
		pl->retrn->count++;
	}
	return new_path;
}

static
void
goto_page(nid,pid,nl)
	int nid;
	int pid;
	VnsNotebook *nl;
{
	VnsAddPage(nl,pid,(struct return_page *)NULL,(char *)NULL,(VnsPage *)NULL);
}

/*ARGSUSED*/
static
void
sel_goto_page(wd,pl,call_data)
	Widget wd;
	VnsPage *pl;
	caddr_t call_data;
{
	VnsNotebook *nl = PageToNote(pl);

	VtGotoPage(pl->pwd,PageToSrv(pl),nl->nid,goto_page,nl);
}

/*ARGSUSED*/
static
void
sel_next_page(wd,pl,cs)
	Widget wd;
	VnsPage *pl;
	XmAnyCallbackStruct *cs;
{
	VnsNotebook *nl = PageToNote(pl);
	long *pages;
	long npages;

	if (srv_inq_notebook_pages(PageToSrv(pl),nl->nid,&pages,&npages) >= 0)
	{
		if (npages)
		{
			if (cs->event != NULL)
			{
				VnsAddPage(nl,find_next_page(pages,(int)npages,pl->pid),
					add_page_to_path(pl),(char *)NULL,
					(cs->event->xbutton.state & ShiftMask) ? pl : NULL);
				free((char *)pages);
				return;
			}

			/* default action */
			VnsAddPage(nl,find_next_page(pages,(int)npages,pl->pid),
				add_page_to_path(pl),(char *)NULL,(VnsPage *)NULL);
			free((char *)pages);
		}
	}
}

/*ARGSUSED*/
static
void
sel_prev_page(wd,pl,cs)
	Widget wd;
	VnsPage *pl;
	XmAnyCallbackStruct *cs;
{
	VnsNotebook *nl = PageToNote(pl);
	long *pages;
	long npages;

	if (srv_inq_notebook_pages(PageToSrv(pl),nl->nid,&pages,&npages) >= 0)
	{
		if (npages)
		{
			if (cs->event != NULL)
			{
				VnsAddPage(nl,find_prev_page(pages,(int)npages,pl->pid),
					add_page_to_path(pl),(char *)NULL,
					(cs->event->xbutton.state & ShiftMask) ? pl : NULL);
				free((char *)pages);
				return;
			}

			/* default action */
			VnsAddPage(nl,find_prev_page(pages,(int)npages,pl->pid),
				add_page_to_path(pl),(char *)NULL,(VnsPage *)NULL);
			free((char *)pages);
		}
	}
}

/*
 * callback for editting properties under the Edit menu
 */
/*ARGSUSED*/
static
void
sel_object_props(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	if (pl->nselected <= 0)   /* no objects selected */
	{
		VtMessage(pl->pwd,"No object is currently selected.");
	}
	else if (pl->nselected == 1)
	{
		VnsObject *obj = get_sel_object(pl);

		if (obj == NULL) return;

		VnsPopupPropertyManager(ObjectToVns(obj));
		VnsPropObject(obj);
	}
	else
	{
		VtMessage(pl->pwd,"To edit object properties, select one object");
	}
}

/*
 * callback for Copy under the Edit menu
 */
/*ARGSUSED*/
static
void
sel_object_copy(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	vns_watch_on(PageToVns(pl));
	copy_objects(pl);
	vns_watch_off(PageToVns(pl));
}

/*ARGSUSED*/
static
void
sel_select_all(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	select_page(pl);
}

/*
 * Callback for handling selection info.
 */
/*ARGSUSED*/
static
void
GotSelection(w,pl,selection,type,value,length,format)
	Widget w;
	VnsPage *pl;
	Atom *selection;
	Atom *type;
	char *value;
	unsigned long *length;
	int *format;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);

	/* check if the user has write permission for the notebook */
	if (!nl->info.can_write) return;

	if (*type == XT_CONVERT_FAIL)
	{
		return;
	}
	else if ((*type == XA_STRING) && *length && (value != NULL))
	{
		/*
		 * if no objects are selected on a page, paste at location of
		 * the vns background cursor.
		 */
		if (pl->nselected <= 0)
		{
			VnsObject *obj;
			Object_Block block;
			int x,y;

			vns_watch_on(vns);

			/* obtain the current location of the cross hairs on the vns background */
			XtVaGetValues(pl->comp,
				XtNlocX,&x,
				XtNlocY,&y,
				NULL);

			/* fill in block information for text object */
			block.info.type = OT_TXT;
			block.info.x = x;
			block.info.y = y;
			block.info.width = 200;
			block.info.height = 200;
			strcpy(block.info.data.t_info.font,vns->last_font);
			strcpy(block.info.data.t_info.color,vns->last_color);
			block.txt = malloc((unsigned)(*length + 1));
			strncpy(block.txt,value,(int)*length);
			block.txt[*length] = 0;

			/* create the text object in the database */
			block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);

			srv_set_object_text(PageToSrv(pl),nl->nid,pl->pid,block.oid,
				block.txt);

			obj = add_object2(pl,&block);

			select_object(obj);

			update_page_scrollbars(pl);

			free(block.txt);
			XtFree(value);

			vns_watch_off(vns);
		}
		else if (pl->nselected == 1)
		{
			VnsObject *obj;

			/* check if the object selected is a text object */
			obj = pl->first_object;
			while(obj != NULL)
			{
				if (obj->selected)
				{
					break;
				}
				obj = obj->next;
			}

			/*
			 * if the object is a text object, then we send the object
			 * a client message event so that it can read the selection.
			 * Note that this approach requires special communication with
			 * the text object since it must interpret the clinet message
			 * event.
			 */
			if (obj->info.type == OT_TXT)
			{
				XClientMessageEvent event;
				event.type = ClientMessage;
				event.display = XtDisplay(w);
				event.window = XtWindow(obj->auxil.txt.wd);
				event.message_type = XInternAtom(XtDisplay(w),"VNS_DRAG_DROP",False);
				event.format = 32;
				event.data.l[0] = 0;
				event.data.l[1] = 0;
				event.data.l[2] = 0;
				event.data.l[3] = event.window;
				event.data.l[4] = XInternAtom(XtDisplay(w),"NOTEBOOK_DROP_TEXT",False);
				XSendEvent(XtDisplay(w),event.window,True,0L,&event);
			}
			else
			{
				VtMessage(pl->pwd,"To X Paste text, select one text object or position the cursor on the background");
			}
		}
		else
		{
			VtMessage(pl->pwd,"To X Paste text, select one text object or position the cursor on the background");
		}
	}
	/* images always get pasted at the cursor location on the vns background */
	else if ((*type == XA_PIXMAP) && *length && (value != NULL))
	{
		int x,y;
		Pixmap p = *((Pixmap *)value);
		XImage *image;
		Object_Block block;
		Window root;
		unsigned width,height,bw,depth;
		VnsObject *obj;

		vns_watch_on(vns);

		/* obtain the image */
		XGetGeometry(XtDisplay(w),p,&root,&x,&y,&width,&height,&bw,&depth);
		image = XGetImage(XtDisplay(w),p,0,0,(unsigned)width,(unsigned)height,
			(unsigned long)AllPlanes,ZPixmap);
		VtGetImgFromImage(vns->dpy,image,&block.img);

		/* obtain the current location of the cross hairs on the vns background */
		XtVaGetValues(pl->comp,
			XtNlocX,&x,
			XtNlocY,&y,
			NULL);

		/* fill in block information for image object */
		block.info.x = x;
		block.info.y = y;
		block.info.width = width;
		block.info.height = height;
		block.info.type = OT_IMAGE;
		block.info.data.i_info.low_res_sw = 0;
		block.info.data.i_info.create_cmap = 0;
		block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);

		srv_set_object_img(PageToSrv(pl),nl->nid,pl->pid,block.oid,
			&block.img);

		obj = add_object2(pl,&block);

		select_object(obj);

		update_page_scrollbars(pl);

		XtFree(value);

		vns_watch_off(vns);
	}
}

/*
 * This event handler handles client message events
 * for the page background.
 */
/*ARGSUSED*/
static
void
CMProc(w,pl,event)
	Widget w;
	VnsPage *pl;
	XClientMessageEvent *event;
{
	if (event->type == ClientMessage)
	{
		if (event->message_type ==
			XInternAtom(XtDisplay(w),"VNS_DRAG_DROP",False))
		{
			int x;
			int y;
			Window child;
			XTranslateCoordinates(XtDisplay(w),(Window)event->data.l[3],
				XtWindow(pl->comp),(int)event->data.l[1],(int)event->data.l[2],&x,&y,&child);
			XtVaSetValues(pl->comp,
				XtNlocX,x,
				XtNlocY,y,
				NULL);
			if (event->data.l[4] == 
				XInternAtom(XtDisplay(w),"NOTEBOOK_DROP_TEXT",False))
			{
				XtGetSelectionValue(pl->comp,XA_PRIMARY,XA_STRING,GotSelection,
					(XtPointer)pl,CurrentTime);
			}
			else if (event->data.l[4] == 
				XInternAtom(XtDisplay(w),"NOTEBOOK_DROP_IMAGE",False))
			{
				XtGetSelectionValue(pl->comp,XA_PRIMARY,XA_PIXMAP,GotSelection,
					(XtPointer)pl,CurrentTime);
			}
		}
		else if (event->message_type ==
			XInternAtom(XtDisplay(w),"NOTEBOOK_DROP_TEXT",False))
		{
			int x;
			int y;
			Window child;
			XTranslateCoordinates(XtDisplay(w),(Window)event->data.l[3],
				XtWindow(pl->comp),(int)event->data.l[1],(int)event->data.l[2],&x,&y,&child);
			XtVaSetValues(pl->comp,
				XtNlocX,x,
				XtNlocY,y,
				NULL);
			XtGetSelectionValue(pl->comp,
				XA_PRIMARY,XA_STRING,GotSelection,
				(XtPointer)pl,CurrentTime);
		}
		else if (event->message_type ==
			XInternAtom(XtDisplay(w),"NOTEBOOK_DROP_IMAGE",False))
		{
			int x;
			int y;
			Window child;
			XTranslateCoordinates(XtDisplay(w),(Window)event->data.l[3],
				XtWindow(pl->comp),(int)event->data.l[1],(int)event->data.l[2],&x,&y,&child);
			XtVaSetValues(pl->comp,
				XtNlocX,x,
				XtNlocY,y,
				NULL);
			XtGetSelectionValue(pl->comp,
				XA_PRIMARY,XA_PIXMAP,GotSelection,
				(XtPointer)pl,CurrentTime);
		}
	}
}

/*ARGSUSED*/
static
void
sel_special_find(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);

	XtMapWidget(vns->toplevel);
	XRaiseWindow(XtDisplay(vns->toplevel),XtWindow(vns->toplevel));
}

/*ARGSUSED*/
static
void
sel_special_xpaste(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	XtGetSelectionValue(pl->comp,XA_PRIMARY,XA_STRING,GotSelection,
		(XtPointer)pl,CurrentTime);
	XtGetSelectionValue(pl->comp,XA_PRIMARY,XA_PIXMAP,GotSelection,
		(XtPointer)pl,CurrentTime);
}

/*ARGSUSED*/
static
void
sel_special_search(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsNotebook *nl = PageToNote(pl);
	VtSearchNotebook(pl->pwd,PageToSrv(pl),nl->nid,goto_page,(XtPointer)nl);
}

/*ARGSUSED*/
static
void
sel_special_bugs(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsNotebook *nl = PageToNote(pl);
	char version[50];

	sprintf(version,"vns%d.%d.%d",VNS_MAJOR_VERSION,VNS_MINOR_VERSION,VNS_RELEASE);

	VtSendBugReport(pl->pwd,PageToSrv(pl),nl->nid,pl->pid,version);
}

static
yes_delete_page(pl,state)
	VnsPage *pl;
	Boolean state;
{
	VnsNotebook *nl = PageToNote(pl);
	int nid = nl->nid;
	int pid = pl->pid;
	if (state)
	{
		delete_page_refs(pl);
		VnsCallCallbacks(PageToVns(pl),CB_PAGE_DELETE,(VnsPointer)pl);
		del_page(pl);
		srv_delete_page(NoteToSrv(nl),nid,pid,1);
	}
	else
	{
		update_page_refs(pl);
		VnsCallCallbacks(PageToVns(pl),CB_PAGE_DELETE,(VnsPointer)pl);
		del_page(pl);
		srv_delete_page(NoteToSrv(nl),nid,pid,0);
	}
	find_last_page_of_note(nl);
}

/*ARGSUSED*/
static
void
sel_page_delete(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VnsNotebook *nl = PageToNote(pl);

	if (nl != NULL)
	{
		/* restrict the user from deleting the home page */
		if (nl->info.home_page == pl->pid)
		{
			VtMessage(pl->pwd,"Deletion of the Home Page is not currently allowed");
		}
		else
		{
			char msg[256];
			sprintf(msg,"Delete '%s' and all of it's objects?",pl->info.title);
			VtDeletePage(pl->pwd,msg,yes_delete_page,(XtPointer)pl);
		}
	}
}

/*
 * Callback when the user has clicked on the open background
 * of a page (i.e., the user did not click on an object). In
 * this case, we want to deselect all objects everywhere.
 */
/*ARGSUSED*/
static
void
sel_comp(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	/* deselect everything when click on white space */
	deselect_all(PageToVns(pl));

	/* update the property manager for the selected page */
	VnsPropPage(pl);
}

/*ARGSUSED*/
static
void
key_hit(wd,pl,buf)
	Widget wd;
	VnsPage *pl;
	char *buf;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);

	vns_watch_on(vns);

	/* only create a text object if we have write access */
	if (nl->info.can_write)
	{
		VnsObject *obj;
		Object_Block block;
		int x,y;

		/* obtain the location of the cross hairs on the vns background */
		XtVaGetValues(pl->comp,
			XtNlocX,&x,
			XtNlocY,&y,
			NULL);

		block.info.type = OT_TXT;
		block.info.x = x;
		block.info.y = y;
		block.info.width = 100;
		block.info.height = 100;
		block.txt = buf;
		strcpy(block.info.data.t_info.font,vns->last_font);
		strcpy(block.info.data.t_info.color,"black");

		/* create the text object in the database */
		block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);

		/* add the object to the page */
		obj = add_object2(pl,&block);

		/* select the object */
		select_object(obj);

		update_page_scrollbars(pl);

		/* place the cursor at the end of the initial text */
		VnsTextPlaceCursor(obj->auxil.txt.wd);
	}
	vns_watch_off(vns);
}

/*
 * Callback that checks the state of the tunnel toggle so that
 * the stacking page toggle can be unset if needed.
 */
/*ARGSUSED*/
static
void
sel_tunnel(wd,pl,tog)
	Widget wd;
	VnsPage *pl;
	XmToggleButtonCallbackStruct *tog;
{
	VnsContext *vns = PageToVns(pl);

	/* deactivate stacking page if active */
	if (vns->stacking_page != NULL)
	{
		XtVaSetValues(vns->stacking_page->stack,
			XmNset,False,
			NULL);
		vns->stacking_page = NULL;
	}

	/* deactivate tunneling page if active */
	if (vns->tunnel_page != NULL)
	{
		XtVaSetValues(vns->tunnel_page->tunnel,
			XmNset,False,
			NULL);
		vns->tunnel_page = NULL;
	}

	/* set the new tunnel page if toggle is set */
	if (tog->set)
	{
		vns->tunnel_page = pl;
	}
}

/*
 * Callback that checks the state of the stacking toggle so that
 * the tunnel page toggle can be unset if needed.
 */
/*ARGSUSED*/
static
void
sel_stack(wd,pl,tog)
	Widget wd;
	VnsPage *pl;
	XmToggleButtonCallbackStruct *tog;
{
	VnsContext *vns = PageToVns(pl);

	/* deactivate stacking page if active */
	if (vns->stacking_page != NULL)
	{
		XtVaSetValues(vns->stacking_page->stack,
			XmNset,False,
			NULL);
		vns->stacking_page = NULL;
	}

	/* deactivate tunneling page if active */
	if (vns->tunnel_page != NULL)
	{
		XtVaSetValues(vns->tunnel_page->tunnel,
			XmNset,False,
			NULL);
		vns->tunnel_page = NULL;
	}

	/* set the new stacking page if toggle is set */
	if (tog->set)
	{
		vns->stacking_page = pl;
	}
}

static
cb_text(pl,file_name)
	VnsPage *pl;
	char *file_name;
{
	VnsContext *vns = PageToVns(pl);
	VnsNotebook *nl = PageToNote(pl);
	int fd;

	vns_watch_on(vns);

	fd = open(file_name,O_RDONLY,0);
	if (fd >= 0)
	{
		struct stat st;
		if (fstat(fd,&st) >= 0)
		{
			Object_Block block;
			int x,y;
			int size = st.st_size;
			char *s = malloc((unsigned)(size + 1));
			read(fd,s,size);
			s[size] = 0;

			/* find the current location of cross hairs on the page */
			XtVaGetValues(pl->comp,
				XtNlocX,&x,
				XtNlocY,&y,
				NULL);

			/* fill in block info */
			block.info.x = x;
			block.info.y = y;
			block.info.width = 200;
			block.info.height = 200;
			block.info.type = OT_TXT;
			block.txt = s;
			strcpy(block.info.data.t_info.font,vns->last_font);
			strcpy(block.info.data.t_info.color,vns->last_color);

			/* create object in the database */
			block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);

			srv_set_object_text(PageToSrv(pl),nl->nid,pl->pid,block.oid,block.txt);

			{
				VnsObject *obj = add_object2(pl,&block);
				select_object(obj);
			}
			free(block.txt);
		}
		close(fd);
	}
	vns_watch_off(vns);
}

/*ARGSUSED*/
static
void
sel_import_text(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VtFileBrowser(pl->pwd,cb_text,(XtPointer)pl);
}

static
cb_image(pl,file_name)
	VnsPage *pl;
	char *file_name;
{
	VnsNotebook *nl = PageToNote(pl);
	Object_Block block;

	vns_watch_on(PageToVns(pl));

	if (read_rast_img(file_name,&block.img) >= 0)
	{
		VnsObject *obj;
		int x,y;

		/* obtain the current location on the page */
		XtVaGetValues(pl->comp,
			XtNlocX,&x,
			XtNlocY,&y,
			NULL);

		/* fill in block information for image object */
		block.info.x = x;
		block.info.y = y;
		block.info.width = block.img.width;
		block.info.height = block.img.height;
		block.info.type = OT_IMAGE;
		block.info.data.i_info.low_res_sw = 0;
		block.info.data.i_info.create_cmap = 0;

		/* create object in database */
		block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);

		srv_set_object_img(PageToSrv(pl),nl->nid,pl->pid,block.oid,&block.img);
		obj = add_object2(pl,&block);
		select_object(obj);
		set_page_colormaps(pl);
	}
	vns_watch_off(PageToVns(pl));
}

/*ARGSUSED*/
static
void
sel_import_image(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	VtFileBrowser(pl->pwd,cb_image,(XtPointer)pl);
}

static
exp_text(obj,file_name)
	VnsObject *obj;
	char *file_name;
{
	int fd;
	vns_watch_on(ObjectToVns(obj));
	fd = open(file_name,O_WRONLY | O_CREAT | O_TRUNC,0600L);
	if (fd < 0)
	{
		char msg[256];
		sprintf(msg,"Error opening export file: %s",file_name);
		VtError(obj->parent->pwd,msg);
	}
	else
	{
		if (obj->info.type == OT_TXT)
		{
			char *text;
			int size;
			text = srv_get_object_text(ObjectToSrv(obj),obj->parent->parent->nid,obj->parent->pid,
				obj->oid);
			size = strlen(text);
			write(fd,text,size);
			free(text);
		}
		close(fd);
	}
	vns_watch_off(ObjectToVns(obj));
}

static
exp_image(obj,file_name)
	VnsObject *obj;
	char *file_name;
{
	if (obj->info.type == OT_IMAGE)
	{
		Rast_Img image;

		srv_get_object_img(ObjectToSrv(obj),obj->parent->parent->nid,obj->parent->pid,
			obj->oid,&image);

		if (write_rast_img(file_name,&image) < 0)
		{
			VtError(obj->parent->pwd,"Error exporting image to file.");
		}
	}
}

/*ARGSUSED*/
static
void
sel_export(wd,pl)
	Widget wd;
	VnsPage *pl;
{
	if (pl->nselected <= 0) 
	{
		VtMessage(pl->pwd,"To export an object, select a single object");
	}
	else if (pl->nselected == 1)
	{
		VnsObject *obj = get_sel_object(pl);

		if (obj == NULL) return;

		switch(obj->info.type)
		{
			case OT_TXT:
				VtFileExport(pl->pwd,"Text Object",exp_text,(XtPointer)obj);
				break;
			case OT_IMAGE:
				VtFileExport(pl->pwd,"Image Object",exp_image,(XtPointer)obj);
				break;
			default:
				VtMessage(pl->pwd,"The object selected is not currently supported for exporting");
				break;
		}
	}
	else
	{
		VtMessage(pl->pwd,"To export an object, select a single object");
	}
}

static
void
PopupObjectMenu(w,pl)
	Widget w;
	VnsPage *pl;
{
	VnsContext *vns = PageToVns(pl);
	VnsObject *obj = get_sel_object(pl);
	VnsNotebook *nl = PageToNote(pl);
	Widget cut = XtNameToWidget(w,"*Cut");
	Widget copy = XtNameToWidget(w,"*Copy");
	Widget paste = XtNameToWidget(w,"*Paste");
	Widget import_image = XtNameToWidget(w,"*ImportImage");
	Widget import_text = XtNameToWidget(w,"*ImportText");

	if (vns->clip_board == NULL)
	{
		XtSetSensitive(paste,False);
	}
	else
	{
		XtSetSensitive(paste,True);
	}

	if (obj == NULL)
	{
		XtSetSensitive(cut,False);
		XtSetSensitive(copy,False);
	}
	else
	{
		if (nl->info.can_write)
		{
			XtSetSensitive(cut,True);
		}
		else
		{
			XtSetSensitive(cut,False);
		}	
		XtSetSensitive(copy,True);
	}

	if (nl->info.can_write)
	{
		XtSetSensitive(import_image,True);
		XtSetSensitive(import_text,True);
	}
	else
	{
		XtSetSensitive(import_image,False);
		XtSetSensitive(import_text,False);
	}
}

static
void
comp_resized(w,pl,info)
	Widget w;
	VnsPage *pl;
	VnsBackgroundStruct *info;
{
	Boolean h_needed = FALSE;
	Boolean v_needed = FALSE;
	int min_w, min_h;
	int act_w, act_h;
	int pad_w, pad_h;
	int val;

	min_w = info->min_width;
	min_h = info->min_height;
	act_w = pl->sw->core.width - 6;
	act_h = pl->sw->core.height - 6;
	pad_w = pl->vbar->core.width + 1;
	pad_h = pl->hbar->core.width + 1;

	/* determine if any scrollbars are needed initially */
	if (min_w > act_w)
	{
		h_needed = TRUE;
	}
	if (min_h > act_h)
	{
		v_needed = TRUE;
	}

	/* now make adjustments taking scrollbar sizes into account */
	if (h_needed && (v_needed == FALSE))
	{
		if (min_h > act_h - pad_h)
		{
			v_needed = TRUE;
		}
	}
	if (v_needed && (h_needed == FALSE))
	{
		if (min_w > act_w - pad_w)
		{
			v_needed = TRUE;
		}
	}

	if (h_needed)
	{
		XtManageChild(pl->hbar);	
	}
	else
	{
		XtVaGetValues(pl->hbar,
			XmNvalue,&val,
			NULL);

		if (val == 0)
		{
			XtUnmanageChild(pl->hbar);
		}
	}

	if (v_needed)
	{
		XtManageChild(pl->vbar);
	}
	else
	{
		XtVaGetValues(pl->vbar,
			XmNvalue,&val,
			NULL);

		if (val == 0)
		{
			XtUnmanageChild(pl->vbar);
		}
	}

	info->viewport_width = act_w;
	info->viewport_height = act_h;
}

/*
 * This public function has been added so that the page
 * scrollbars could be properly activated if an object
 * that is either larger than the page is added or a 
 * new object is added off the page by some remote
 * application.
 */
update_page_scrollbars(pl)
	VnsPage *pl;
{
	Boolean h_needed = FALSE;
	Boolean v_needed = FALSE;
	int min_w, min_h;
	int act_w, act_h;
	int pad_w, pad_h;

	calc_minimum_box(pl->comp,&min_w,&min_h);

	act_w = pl->sw->core.width - 6;
	act_h = pl->sw->core.height - 6;
	pad_w = pl->vbar->core.width + 1;
	pad_h = pl->hbar->core.width + 1;

	/* determine if any scrollbars are needed initially */
	if (min_w > act_w)
	{
		h_needed = TRUE;
	}
	if (min_h > act_h)
	{
		v_needed = TRUE;
	}

	/* now make adjustments taking scrollbar sizes into account */
	if (h_needed && (v_needed == FALSE))
	{
		if (min_h > act_h - pad_h)
		{
			v_needed = TRUE;
		}
	}
	if (v_needed && (h_needed == FALSE))
	{
		if (min_w > act_w - pad_w)
		{
			v_needed = TRUE;
		}
	}

	if (h_needed)
	{
		XtManageChild(pl->hbar);
	}
	else
	{
		XtUnmanageChild(pl->hbar);
	}

	if (v_needed)
	{
		XtManageChild(pl->vbar);
	}
	else
	{
		XtUnmanageChild(pl->vbar);
	}

	XtVaSetValues(pl->comp,
		XmNwidth,act_w,
		XmNheight,act_h,
		NULL);
}

static
void
sw_resized(w,pl)
	Widget w;
	VnsPage *pl;
{
	VnsNotebook *nl = PageToNote(pl);
	Boolean h_needed = FALSE;
	Boolean v_needed = FALSE;
	int min_w, min_h;
	int act_w, act_h;
	int pad_w, pad_h;

	/* set the new page size */
	if (pl->info.width != w->core.width ||
		pl->info.height != w->core.height)
	{
		pl->info.width = w->core.width;
		pl->info.height = w->core.height;
		srv_modify_page(PageToSrv(pl),nl->nid,pl->pid,&pl->info);
	}

	calc_minimum_box(pl->comp,&min_w,&min_h);

	act_w = w->core.width - 6;
	act_h = w->core.height - 6;
	pad_w = pl->vbar->core.width + 1;
	pad_h = pl->hbar->core.width + 1;

	/* determine if any scrollbars are needed initially */
	if (min_w > act_w)
	{
		h_needed = TRUE;
	}
	if (min_h > act_h)
	{
		v_needed = TRUE;
	}

	/* now make adjustments taking scrollbar sizes into account */
	if (h_needed && (v_needed == FALSE))
	{
		if (min_h > act_h - pad_h)
		{
			v_needed = TRUE;
		}
	}
	if (v_needed && (h_needed == FALSE))
	{
		if (min_w > act_w - pad_w)
		{
			v_needed = TRUE;
		}
	}

	if (h_needed)
	{
		XtManageChild(pl->hbar);
	}
	else
	{
		XtUnmanageChild(pl->hbar);
	}

	if (v_needed)
	{
		XtManageChild(pl->vbar);
	}
	else
	{
		XtUnmanageChild(pl->vbar);
	}

	XtVaSetValues(pl->comp,
		XmNwidth,act_w,
		XmNheight,act_h,
		NULL);
}

/* Adds page to notebook */
VnsPage *
VnsAddPage(nl,page,path,page_geom,opage)
	VnsNotebook *nl;
	int page;
	struct return_page *path;
	char *page_geom;
	VnsPage *opage;
{
	VnsContext *vns = NoteToVns(nl);
	VnsPage *stack_page = NULL;
	VnsPage *new;
	XmString xm_str;
	Widget home;
	Arg args[10];
	int n;
	int nid;

	vns_watch_on(vns);

	nid = nl->nid;

	new = *(VnsFindPage(nl,page));
	if (new == NULL)
	{
		Object_Block *blocks;
		int nblocks;
		VnsPage *old_page = NULL;

		/* if opage is given, then use it */
		if (opage != NULL)
		{
			old_page = opage;
		}
		else
		{
			old_page = vns->tunnel_page;
		}
		if (old_page == NULL)
		{
			new = (VnsPage *)malloc(sizeof *new);
			new->selected_object = NULL;
			new->nselected = 0;
			new->first_object = NULL;
		}
		else
		{
			VnsPage **p = VnsFindPage(old_page->parent,old_page->pid);
			if (*p == NULL)
			{
				fprintf(stderr,"You should not get this message, please report it\n");
			}
			{
				int val,size,d1,d2;

				XmScrollBarGetValues(old_page->hbar,&val,&size,&d1,&d2);
				XmScrollBarSetValues(old_page->hbar,0,size,d1,d2,True);
				XmScrollBarGetValues(old_page->vbar,&val,&size,&d1,&d2);
				XmScrollBarSetValues(old_page->vbar,0,size,d1,d2,True);
			}
			VtUnmanageChildren(old_page->comp);
			while(old_page->first_object != NULL)
			{
				del_object(old_page->first_object);
			}
			*p = old_page->next;
			new = old_page;
		}
		srv_get_blurted_page(NoteToSrv(nl),nid,page,&new->info,&blocks,&nblocks);
		new->col = get_page_color(vns,new->info.color);
		new->parent = nl;
		new->pid = page;
		if (old_page == NULL)
		{
			char *geom = NULL;
			int x,y;

			/* if there is a stacking page, then adjust x,y corrdinates */
			stack_page = vns->stacking_page;
			if (stack_page != NULL)
			{
				sprintf(geom = new->geom,"+%d+%d",
					x = stack_page->pwd->core.x + 20,
					y = stack_page->pwd->core.y + 20);

				/* unset the stacking page toggle for old stacking page */
				XtVaSetValues(stack_page->stack,
					XmNset,False,
					NULL);

				/* propagate the new stacking page to this page */
				vns->stacking_page = new;
			}
			else
			{
				x = y = 0;
			}

			new->pwd = XtVaCreatePopupShell("PageShell",topLevelShellWidgetClass,vns->toplevel,
				XmNx,x,
				XmNy,y,
				XmNtitle,new->info.title,
				XmNallowShellResize,TRUE,
				XmNiconPixmap,page_bitmap,
				XmNiconMask,page_bitmap,
				XmNiconName,new->info.title,
				XmNwindowGroup,None,
				XmNgeometry,(geom == NULL) ? page_geom : NULL,
				XmNtransient,False,
				NULL);
			XtAddCallback(new->pwd,XmNdestroyCallback,sel_del_win_page,(XtPointer)new);
		}
		else
		{
			XtVaSetValues(new->pwd,
				XmNtitle,new->info.title,
				XmNiconName,new->info.title,
				NULL);
		}
		if (old_page == NULL)
		{
			new->form = XtVaCreateManagedWidget("PageForm",vListWidgetClass,new->pwd,
				XtNrowPivot,2,
				XtNvSpacing,2,
				XtNhSpacing,2,
				NULL);

			new->exit_pixmap = XmGetPixmap(XtScreen(new->form),"exit",
				new->form->core.border_pixel,new->form->core.background_pixel);
			new->home_pixmap = XmGetPixmap(XtScreen(new->form),"home",
				new->form->core.border_pixel,new->form->core.background_pixel);
			new->link_here_pixmap = XmGetPixmap(XtScreen(new->form),"link_here",
				new->form->core.border_pixel,new->form->core.background_pixel);
			new->link_here_pixmap_s = XmGetPixmap(XtScreen(new->form),"link_here_rev",
				new->form->core.border_pixel,new->form->core.background_pixel);
			new->return_pixmap = XmGetPixmap(XtScreen(new->form),"return",
				new->form->core.border_pixel,new->form->core.background_pixel);
			new->return_pixmap_s = XmGetPixmap(XtScreen(new->form),"return_rev",
				new->form->core.border_pixel,new->form->core.background_pixel);
			new->tunnel_pixmap = XmGetPixmap(XtScreen(new->form),"tunnel",
				new->form->core.border_pixel,new->form->core.background_pixel);
			new->tunnel_pixmap_s = XmGetPixmap(XtScreen(new->form),"tunnel_rev",
				new->form->core.border_pixel,new->form->core.background_pixel);
			new->stack_pixmap = XmGetPixmap(XtScreen(new->form),"stack",
				new->form->core.border_pixel,new->form->core.background_pixel);
			new->stack_pixmap_s = XmGetPixmap(XtScreen(new->form),"stack_rev",
				new->form->core.border_pixel,new->form->core.background_pixel);
		}
		{
			Widget lft;
			Widget rgt;
			if (old_page == NULL)
			{
				new->header = XtVaCreateManagedWidget("PageHeader",vListWidgetClass,new->form,
					XtNstretchHoriz,True,
					XtNnumRows,1,
/*
					XtNhSpacing,2,
*/
					NULL);
				lft = XtVaCreateManagedWidget("LeftHeader",xmRowColumnWidgetClass,new->header,
					XmNrowColumnType,XmMENU_BAR,
					XmNorientation,XmHORIZONTAL,
					NULL);
				rgt = XtVaCreateManagedWidget("RightTop",xmFormWidgetClass,new->header,
					XtNhAlign,VL_RIGHT,
					NULL);
			}

			if (old_page == NULL)
			{
				Widget menu,button,txt,img,pag,lnk,alnk;

				n = 0;
				XtSetArg(args[n],XmNmarginWidth,2); n++;
				XtSetArg(args[n],XmNmarginHeight,2); n++;
				menu = XmCreatePulldownMenu(lft,"Menu",args,n);

				n = 0;
				XtSetArg(args[n],XmNsubMenuId,menu); n++;
				XtSetArg(args[n],XmNmarginHeight,1); n++;
				XtSetArg(args[n],XmNmnemonic,'C'); n++;
				new->w_create_menu = XmCreateCascadeButton(lft,"Create",args,n);

				txt = XtVaCreateManagedWidget("Text",xmPushButtonGadgetClass,menu,
					NULL);
				img = XtVaCreateManagedWidget("Image",xmPushButtonGadgetClass,menu,
					NULL);
				pag = XtVaCreateManagedWidget("Page",xmPushButtonGadgetClass,menu,
					NULL);
				lnk = XtVaCreateManagedWidget("Link",xmPushButtonGadgetClass,menu,
					NULL);
				alnk = XtVaCreateManagedWidget("Action",xmPushButtonGadgetClass,menu,
					NULL);

				XtAddCallback(txt,XmNactivateCallback,sel_create_text,(XtPointer)new);
				XtAddCallback(img,XmNactivateCallback,sel_create_img,(XtPointer)new);
				XtAddCallback(pag,XmNactivateCallback,sel_create_page,(XtPointer)new);
				XtAddCallback(lnk,XmNactivateCallback,sel_create_link,(XtPointer)new);
				XtAddCallback(alnk,XmNactivateCallback,sel_create_alink,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				Widget menu,import_text,import_image,export;

				n = 0;
				XtSetArg(args[n],XmNmarginWidth,2); n++;
				XtSetArg(args[n],XmNmarginHeight,2); n++;
				menu = XmCreatePulldownMenu(lft,"Menu",args,n);

				n = 0;
				XtSetArg(args[n],XmNsubMenuId,menu); n++;
				XtSetArg(args[n],XmNmarginHeight,1); n++;
				XtSetArg(args[n],XmNmnemonic,'F'); n++;
				new->w_import_menu = XmCreateCascadeButton(lft,"File",args,n);

				xm_str = XmStringCreateLtoR("Import Text ...",XmSTRING_DEFAULT_CHARSET);
				import_text = XtVaCreateManagedWidget("ImportText",xmPushButtonGadgetClass,menu,
					XmNlabelString,xm_str,
					NULL);
				XmStringFree(xm_str);

				xm_str = XmStringCreateLtoR("Import Image ...",XmSTRING_DEFAULT_CHARSET);
				import_image = XtVaCreateManagedWidget("ImportImage",xmPushButtonGadgetClass,menu,
					XmNlabelString,xm_str,
					NULL);
				XmStringFree(xm_str);

				export = XtVaCreateManagedWidget("Export ...",xmPushButtonGadgetClass,menu,
					NULL);

				XtAddCallback(import_text,XmNactivateCallback,sel_import_text,(XtPointer)new);
				XtAddCallback(import_image,XmNactivateCallback,sel_import_image,(XtPointer)new);
				XtAddCallback(export,XmNactivateCallback,sel_export,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				Widget menu, prop, copy, select_all;

				n = 0;
				XtSetArg(args[n],XmNmarginWidth,2); n++;
				XtSetArg(args[n],XmNmarginHeight,2); n++;
				menu = XmCreatePulldownMenu(lft,"Menu",args,n);
				XtAddCallback(XtParent(menu),XmNpopupCallback,PopupObjectMenu,(XtPointer)new);

				n = 0;
				XtSetArg(args[n],XmNsubMenuId,menu); n++;
				XtSetArg(args[n],XmNmarginHeight,1); n++;
				XtSetArg(args[n],XmNmnemonic,'O'); n++;
				new->w_edit = XmCreateCascadeButton(lft,"Object",args,n);

				new->w_undo = XtVaCreateManagedWidget("Undo",xmPushButtonGadgetClass,menu,
					XmNsensitive,False,
					NULL);
				prop = XtVaCreateManagedWidget("Properties ...",xmPushButtonGadgetClass,menu,
					NULL);
				new->w_cut = XtVaCreateManagedWidget("Cut",xmPushButtonGadgetClass,menu,
					NULL);
				copy = XtVaCreateManagedWidget("Copy",xmPushButtonGadgetClass,menu,
					NULL);
				new->w_paste = XtVaCreateManagedWidget("Paste",xmPushButtonGadgetClass,menu,
					NULL);
				select_all = XtVaCreateManagedWidget("Select All",xmPushButtonGadgetClass,menu,
					NULL);

				XtAddCallback(new->w_undo,XmNactivateCallback,sel_object_undo,(XtPointer)new);
				XtAddCallback(prop,XmNactivateCallback,sel_object_props,(XtPointer)new);
				XtAddCallback(new->w_cut,XmNactivateCallback,sel_object_cut,(XtPointer)new);
				XtAddCallback(copy,XmNactivateCallback,sel_object_copy,(XtPointer)new);
				XtAddCallback(new->w_paste,XmNactivateCallback,sel_object_paste,(XtPointer)new);
				XtAddCallback(select_all,XmNactivateCallback,sel_select_all,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				Widget menu, prop, print;

				n = 0;
				XtSetArg(args[n],XmNmarginWidth,2); n++;
				XtSetArg(args[n],XmNmarginHeight,2); n++;
				menu = XmCreatePulldownMenu(lft,"Menu",args,n);

				n = 0;
				XtSetArg(args[n],XmNsubMenuId,menu); n++;
				XtSetArg(args[n],XmNmarginHeight,1); n++;
				XtSetArg(args[n],XmNmnemonic,'P'); n++;
				XmCreateCascadeButton(lft,"Page",args,n);

				new->w_create_page = XtVaCreateManagedWidget("Create",xmPushButtonGadgetClass,menu,
					NULL);
				print = XtVaCreateManagedWidget("Print ...",xmPushButtonGadgetClass,menu,
					NULL);
				prop = XtVaCreateManagedWidget("Properties ...",xmPushButtonGadgetClass,menu,
					NULL);
				new->w_object_del = XtVaCreateManagedWidget("Delete ...",xmPushButtonGadgetClass,menu,
					NULL);

				XtAddCallback(new->w_create_page,XmNactivateCallback,sel_create_page,(XtPointer)new);
				XtAddCallback(print,XmNactivateCallback,sel_page_print,(XtPointer)new);
				XtAddCallback(prop,XmNactivateCallback,sel_page_props,(XtPointer)new);
				XtAddCallback(new->w_object_del,XmNactivateCallback,sel_page_delete,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				Widget menu, fp, search, bug_mail;

				n = 0;
				XtSetArg(args[n],XmNmarginWidth,2); n++;
				XtSetArg(args[n],XmNmarginHeight,2); n++;
				menu = XmCreatePulldownMenu(lft,"Menu",args,n);

				n = 0;
				XtSetArg(args[n],XmNsubMenuId,menu); n++;
				XtSetArg(args[n],XmNmarginHeight,1); n++;
				XtSetArg(args[n],XmNmnemonic,'S'); n++;
				XmCreateCascadeButton(lft,"Special",args,n);

				fp = XtVaCreateManagedWidget("Find Command Panel",xmPushButtonGadgetClass,menu,
					NULL);
				new->w_x_paste = XtVaCreateManagedWidget("XPaste",xmPushButtonGadgetClass,menu,
					NULL);
				search = XtVaCreateManagedWidget("Search ...",xmPushButtonGadgetClass,menu,
					NULL);
				bug_mail = XtVaCreateManagedWidget("Report a Problem ...",xmPushButtonGadgetClass,menu,
					NULL);

				XtAddCallback(fp,XmNactivateCallback,sel_special_find,(XtPointer)new);
				XtAddCallback(new->w_x_paste,XmNactivateCallback,sel_special_xpaste,(XtPointer)new);
				XtAddCallback(search,XmNactivateCallback,sel_special_search,(XtPointer)new);
				XtAddCallback(bug_mail,XmNactivateCallback,sel_special_bugs,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				new->link_here = XtVaCreateWidget("LinkHere",xmPushButtonWidgetClass,rgt,
					XmNlabelType,XmPIXMAP,
					XmNlabelPixmap,new->link_here_pixmap,
					XmNlabelInsensitivePixmap,new->link_here_pixmap_s,
					XmNsensitive,(vns->link_callback != NULL),
					XmNmarginWidth,0,
					XmNmarginHeight,2,
					XmNhighlightThickness,0,
					XmNleftAttachment,XmATTACH_FORM,
					XmNtopAttachment,XmATTACH_FORM,
					XmNbottomAttachment,XmATTACH_FORM,
					NULL);
				XtAddCallback(new->link_here,XmNactivateCallback,sel_link_here,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				new->tunnel = XtVaCreateWidget("Tunnel",xmToggleButtonGadgetClass,rgt,
					XmNlabelType,XmPIXMAP,
					XmNlabelPixmap,new->tunnel_pixmap,
					XmNselectPixmap,new->tunnel_pixmap_s,
					XmNindicatorOn,False,
					XmNshadowThickness,2,
					XmNmarginWidth,0,
					XmNmarginHeight,2,
					XmNhighlightThickness,0,
					XmNleftWidget,new->link_here,
					XmNleftAttachment,XmATTACH_WIDGET,
					XmNtopAttachment,XmATTACH_FORM,
					XmNbottomAttachment,XmATTACH_FORM,
					NULL);
				XtAddCallback(new->tunnel,XmNdisarmCallback,sel_tunnel,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				new->stack = XtVaCreateWidget("Stack",xmToggleButtonGadgetClass,rgt,
					XmNlabelType,XmPIXMAP,
					XmNlabelPixmap,new->stack_pixmap,
					XmNselectPixmap,new->stack_pixmap_s,
					XmNindicatorOn,False,
					XmNshadowThickness,2,
					XmNmarginWidth,0,
					XmNmarginHeight,2,
					XmNhighlightThickness,0,
					XmNset,(vns->stacking_page != NULL),
					XmNleftWidget,new->tunnel,
					XmNleftAttachment,XmATTACH_WIDGET,
					XmNtopAttachment,XmATTACH_FORM,
					XmNbottomAttachment,XmATTACH_FORM,
					NULL);
				XtAddCallback(new->stack,XmNdisarmCallback,sel_stack,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				new->w_retrn = XtVaCreateWidget("Return",xmPushButtonWidgetClass,rgt,
					XmNlabelType,XmPIXMAP,
					XmNlabelPixmap,new->return_pixmap,
					XmNlabelInsensitivePixmap,new->return_pixmap_s,
					XmNmarginWidth,0,
					XmNmarginHeight,2,
					XmNhighlightThickness,0,
					XmNsensitive,(path != NULL),
					XmNleftWidget,new->stack,
					XmNleftAttachment,XmATTACH_WIDGET,
					XmNtopAttachment,XmATTACH_FORM,
					XmNbottomAttachment,XmATTACH_FORM,
					NULL);
				XtAddCallback(new->w_retrn,XmNactivateCallback,sel_return_page,(XtPointer)new);
			}
			else
			{
				XtVaSetValues(new->w_retrn,
					XmNsensitive,(path != NULL),
					NULL);
			}
			if (old_page == NULL)
			{
				home = XtVaCreateWidget("Home",xmPushButtonWidgetClass,rgt,
					XmNlabelType,XmPIXMAP,
					XmNlabelPixmap,new->home_pixmap,
					XmNleftWidget,new->w_retrn,
					XmNmarginWidth,0,
					XmNmarginHeight,2,
					XmNhighlightThickness,0,
					XmNleftAttachment,XmATTACH_WIDGET,
					XmNtopAttachment,XmATTACH_FORM,
					XmNbottomAttachment,XmATTACH_FORM,
					NULL);
				XtAddCallback(home,XmNactivateCallback,sel_home_page,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				Widget close = XtVaCreateWidget("Close",xmPushButtonWidgetClass,rgt,
					XmNlabelType,XmPIXMAP,
					XmNlabelPixmap,new->exit_pixmap,
					XmNmarginWidth,0,
					XmNmarginHeight,2,
					XmNhighlightThickness,0,
					XmNleftWidget,home,
					XmNleftAttachment,XmATTACH_WIDGET,
					XmNtopAttachment,XmATTACH_FORM,
					XmNbottomAttachment,XmATTACH_FORM,
					XmNrightAttachment,XmATTACH_FORM,
					NULL);
				XtAddCallback(close,XmNactivateCallback,sel_close_page,(XtPointer)new);
			}
			VtManageChildren(new->header);
		}
		if (old_page == NULL)
		{
			int page_width = new->info.width;
			int page_height = new->info.height;

			if (page_width < 0)
			{
				page_width = 100;
			}
			if (page_height < 0)
			{
				page_height = 100;
			}
			if (page_width > DisplayWidth(vns->dpy,DefaultScreen(vns->dpy)))
			{
				page_width = DisplayWidth(vns->dpy,DefaultScreen(vns->dpy));
				page_width -= 10;
			}
			if (page_height > DisplayHeight(vns->dpy,DefaultScreen(vns->dpy)))
			{
				page_height = DisplayHeight(vns->dpy,DefaultScreen(vns->dpy));
				page_height -= 10;
			}

			new->sw = XtVaCreateManagedWidget("Sw",xmScrolledWindowWidgetClass,new->form,
				XmNscrollingPolicy,XmAUTOMATIC,
				XtNstretchVert,True,
				XtNstretchHoriz,True,
				XmNheight,page_height,
				XmNwidth,page_width,
				NULL);

			/* get the widget pointers for the scrollbars */
			XtVaGetValues(new->sw,
				XmNverticalScrollBar,&new->vbar,
				XmNhorizontalScrollBar,&new->hbar,
				NULL);

			/* unmanage them for now */
			XtUnmanageChild(new->hbar);
			XtUnmanageChild(new->vbar);

			/* add the page background to the scrolled window */
			new->comp = XtVaCreateManagedWidget("View",
				vnsBackgroundWidgetClass,new->sw,
				XtNbackground,new->col,
				XtNsnapToGrid,vns->session->snap_to_grid,
				XtNshowGrid,vns->session->show_grid,
				XtNshowOutlines,vns->session->show_outlines,
				XtNwidth,page_width - 2,
				XtNheight,page_height - 2,
				NULL);

			/* add an event handler so we can track changes in the scrolled window */
			XtAddEventHandler(new->sw,StructureNotifyMask,False,sw_resized,(XtPointer)new);
			XtAddEventHandler(new->comp,0L,True,CMProc,(XtPointer)new);

			XtAddCallback(new->comp,XtNkeyHit,key_hit,(XtPointer)new);
			XtAddCallback(new->comp,XtNselect,sel_comp,(XtPointer)new);
			XtAddCallback(new->comp,XtNresizeCallback,comp_resized,(XtPointer)new);
		}
		else
		{
			XtVaSetValues(new->comp,
				XtNbackground,new->col,
				XmNwidth,new->sw->core.width - 6,
				XmNheight,new->sw->core.height - 6,
				NULL);
		}
		{
			static char pagenum[128];
			Widget nxt,prv;
			Widget rgt,gt;
			sprintf(pagenum,"  Page %d of %d  ",page,nl->last_page);
			if (old_page == NULL)
			{
				new->footer = XtVaCreateManagedWidget("PageFooter",vListWidgetClass,new->form,
					XtNstretchHoriz,True,
					XtNnumRows,1,
					XtNhSpacing,4,
					NULL);
			}

			if (old_page == NULL)
			{
				xm_str = XmStringCreateLtoR(nl->info.title,XmSTRING_DEFAULT_CHARSET);
				new->nname = XtVaCreateWidget("NoteName",xmLabelWidgetClass,new->footer,
					XmNlabelString,xm_str,
					NULL);
				XmStringFree(xm_str);
			}
			else
			{
				xm_str = XmStringCreateLtoR(nl->info.title,XmSTRING_DEFAULT_CHARSET);
				XtVaSetValues(new->nname,
					XmNlabelString,xm_str,
					NULL);
				XmStringFree(xm_str);
			}
			if (old_page == NULL)
			{
				rgt = XtVaCreateWidget("RightFooter",vListWidgetClass,new->footer,
					XtNnumRows,1,
					XtNhAlign,VL_RIGHT,
					NULL);
				gt = XtVaCreateWidget("GoTo ...",xmPushButtonWidgetClass,rgt,
					NULL);
				XtAddCallback(gt,XmNactivateCallback,sel_goto_page,(XtPointer)new);

				/* add previous page arrow button */
				prv = XtVaCreateWidget("PrevPage",xmArrowButtonWidgetClass,rgt,
					XmNarrowDirection,XmARROW_LEFT,
					NULL);
				XtAddCallback(prv,XmNactivateCallback,sel_prev_page,(XtPointer)new);
			}
			if (old_page == NULL)
			{
				/* add label with page numbers */
				xm_str = XmStringCreateLtoR(pagenum,XmSTRING_DEFAULT_CHARSET);
				new->w_pagenum = XtVaCreateWidget("PageNum",xmLabelWidgetClass,rgt,
					XmNlabelString,xm_str,
					NULL);
				XmStringFree(xm_str);
			}
			else
			{
				xm_str = XmStringCreateLtoR(pagenum,XmSTRING_DEFAULT_CHARSET);
				XtVaSetValues(new->w_pagenum,
					XmNlabelString,xm_str,
					NULL);
				XmStringFree(xm_str);
			}
			if (old_page == NULL)
			{
				nxt = XtVaCreateWidget("NextPage",xmArrowButtonWidgetClass,rgt,
					XmNarrowDirection,XmARROW_RIGHT,
					NULL);
				XtAddCallback(nxt,XmNactivateCallback,sel_next_page,(XtPointer)new);
			}
			VtManageChildren(new->footer);
		}

		/* add the objects to the page */
		if (nblocks)
		{
			int i;
			for(i = 0; i < nblocks; i++)
			{
				add_object2(new,blocks + i);
				switch(blocks[i].info.type)
				{
					case OT_TXT :
						free(blocks[i].txt);
						break;
				}
			}
			free((char *)blocks);
		}

		/* recheck the width and height for the page background and 
		 * remanage the scrollbars as needed.
		 */
		{
			int min_width, min_height;

			calc_minimum_box(new->comp,&min_width,&min_height);

			if (min_width > new->info.width)
			{
				XtManageChild(new->hbar);
			}
			if (min_height > new->info.height)
			{
				XtManageChild(new->vbar);
			}

		}

		/* set the page accesses for the new page (i.e. read-only or read/write) */
		set_page_accesses(new);

		/* popup the new page and set the keyboard focus for the page body */
		XtPopup(new->pwd,XtGrabNone);
		XRaiseWindow(XtDisplay(new->pwd),XtWindow(new->pwd));
		XtSetKeyboardFocus(new->pwd,new->comp);

		VtWatchOn(new->pwd);

		/* insert the new page in the global list of all pages */
		if (old_page == NULL)
		{
			new->npage = vns->first_page;
			vns->first_page = new;
		}

		/* insert the new page in the notebook page list for it's notebook */
		new->next = nl->first_page;
		nl->first_page = new;
	}
	else
	{
		/* map and raise the existing page window */
		XMapWindow(XtDisplay(new->pwd),XtWindow(new->pwd));
		XRaiseWindow(XtDisplay(new->pwd),XtWindow(new->pwd));

		/* if we are stacking the page, propagate the stacking page forward */
		stack_page = vns->stacking_page;
		if (stack_page != NULL && stack_page != new)
		{
			XtVaSetValues(new->pwd,
				XtNx,stack_page->pwd->core.x + 20,
				XtNy,stack_page->pwd->core.y + 20,
				NULL);
			XtVaSetValues(stack_page->stack,
				XmNset,False,
				NULL);
			XtVaSetValues(new->stack,
				XmNset,True,
				NULL);
			vns->stacking_page = new;
		}

		/* set the sensitivity of the return button */
		XtVaSetValues(new->w_retrn,
			XmNsensitive,(path != NULL),
			NULL);
	}

	/* set the return path and increment the count if path exists */
	new->retrn = path;
	if (path != NULL)
	{
		path->count++;
	}

	set_page_colormaps(new);

	/* stack the objects on the page */
	stack_objects(new);

	VtWatchOff(new->pwd);

	vns_watch_off(vns);

	return new;
}
