/*
 * 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: object.c
 * Date: 05/02/91
 *
 * Description:
 *   This file contains the functions for managing objects.
 *
 * Ideas:
 *   The "drivers" used for each object should be split off into
 *   separate files for each object, thus grouping together all
 *   of the necessary functions to manage a given object. Then,
 *   the set of generic object functions should operate on the
 *   drivers that are registered. This will promote new object
 *   development when the API supports new object drivers. But,
 *   storage for new objects will also have to be supported by the
 *   VNS database.
 *
 * Revisions:
 ****************************************************************/
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <Xm/Xm.h>
#include <Xm/Text.h>
#include <Xm/PushB.h>

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

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

#include "VnsP.h"

/* external pixmaps */
extern Pixmap link_bitmap;
extern Pixmap dlink_bitmap;
extern Pixmap badlink_bitmap;
extern Pixmap qmark_bitmap;
extern Pixmap action_link_bitmap;

/*
 * stack a list of obejct for a given page as follows:
 * - images on bottom
 * - text objects in the middle
 * - links (action and page) on top
 */
stack_objects(pl)
	VnsPage *pl;
{
	VnsObject *ol;

	if (pl == NULL) return;

	/* first pass: raise all text objects */
	ol = pl->first_object;
	while (ol != NULL)
	{
		if (ol->info.type == OT_TXT)
		{
			XRaiseWindow(XtDisplay(ol->owd),XtWindow(ol->owd));
		}
		ol = ol->next;
	}

	/* second pass: raise all page links and action links */
	ol = pl->first_object;
	while (ol != NULL)
	{
		if (ol->info.type == OT_LINK || ol->info.type == OT_ACTION_LINK)
		{
			XRaiseWindow(XtDisplay(ol->owd),XtWindow(ol->owd));
		}
		ol = ol->next;
	}
}

/* Finds object on a page */
VnsObject **
VnsFindObject(pl,oid)
	VnsPage *pl;
	int oid;
{
	VnsObject **p = &pl->first_object;

	while(*p != NULL)
	{
		if ((*p)->oid == oid)
		{
			return p;
		}
		p = &((*p)->next);
	}
	return p;
}

/*
 * This callback function handles the traversal of a link object.
 * Currently, the link destination is not implemented as a fully
 * qualified name. It does not include the database name so the
 * current implementation is ambiguous if mutliple database connections
 * are established. But default, the link is considered to reside in
 * the same database as the link object itself.
 */
/*ARGSUSED*/
static
void
select_link(wd,obj,event)
	Widget wd;
	VnsObject *obj;
	XButtonEvent *event;
{
	VnsDatabase *db = ObjectToDb(obj);
	VnsPage *pl = ObjectToPage(obj);

	if (event->button == Button1)
	{
		int nid = (int)obj->info.data.l_info.dest_note;
		int pid = (int)obj->info.data.l_info.dest_page;
		int valid = srv_validate_page(ObjectToSrv(obj),nid,pid);

		if (valid == 1)
		{
			VnsNotebook *nl = *(VnsFindNotebook(db,nid));
			if (nl != NULL)
			{
				if (event->state & ShiftMask)
				{
					VnsAddPage(nl,pid,add_page_to_path(pl),(char *)NULL,pl);
				}
				else
				{
					VnsAddPage(nl,pid,add_page_to_path(pl),
						(char *)NULL,(VnsPage *)NULL);
				}
			}
		}
		else if (valid == 2)
		{
			VtMessage(pl->pwd,"Destination page does not exist.");
		}
		else if (valid == 3)
		{
			VtMessage(pl->pwd,"Destination page exists but you no longer have access.");
		}
	}
}

static
char *
copy_into_buf(s,t)
	char *s;
	char *t;
{
	while(*t)
	{
		*s++ = *t++;
	}
	return s;
}

/*ARGSUSED*/
static
void
select_action_link(wd,ol)
	Widget wd;
	VnsObject *ol;
{
	static char cmd[256];
	char *s = cmd;
	char *t = ol->info.data.al_info.command;

	vns_watch_on(ObjectToVns(ol));

	while(*t != NULL)
	{
		if (*t == '$')
		{
			char value[256];
			char *u = value;
			t++;
			while(*t >= 'a' && *t <= 'z'|| *t >= 'A' && *t <= 'Z')
			{
				*u++ = *t++;
			}
			*u = 0;
			if (!strcmp(value,"DISPLAY"))
			{
				s = copy_into_buf(s,XDisplayString(XtDisplay(wd)));
			}
			else if (!strcmp(value,"NOTEBOOK"))
			{
				VnsNotebook *nl = ObjectToNote(ol);
				char num[256];

				sprintf(num,"%d",nl->nid);
				s = copy_into_buf(s,num);
			}
			else if (!strcmp(value,"PAGE"))
			{
				VnsPage *pl = ObjectToPage(ol);
				char num[256];

				sprintf(num,"%d",pl->pid);
				s = copy_into_buf(s,num);
			}
			else if (!strcmp(value,"OBJECT"))
			{
				char num[256];

				sprintf(num,"%d",ol->oid);
				s = copy_into_buf(s,num);
			}
			else
			{
				char *getenv();
				char *num = getenv(value);
				if (num != NULL)
				{
					s = copy_into_buf(s,num);
				}
			}
		}
		else
		{
			*s++ = *t++;
		}
	}
	*s = 0;
	if (fork() == 0)
	{
		close(ConnectionNumber(XtDisplay(wd)));
		system(cmd);
		exit(0);
	}
	vns_watch_off(ObjectToVns(ol));
}

/*ARGSUSED*/
static
reconfigured(w,obj)
	Widget w;
	VnsObject *obj;
{
	VnsNotebook *nl = ObjectToNote(obj);
	VnsPage *pl = ObjectToPage(obj);
	int new_x = obj->owd->core.x;
	int new_y = obj->owd->core.y;
	int new_width = obj->owd->core.width;
	int new_height = obj->owd->core.height;
	int changed_size;

	obj->info.x = new_x;
	obj->info.y = new_y;
	changed_size = (new_width != obj->info.width || new_height != obj->info.height);
	obj->info.width = new_width;
	obj->info.height = new_height;

	srv_modify_object(ObjectToSrv(obj),nl->nid,pl->pid,obj->oid,&obj->info);

	if (obj->info.type == OT_IMAGE && changed_size)
	{
		XImage *image;
		Colormap cmap;
		XtVaGetValues(obj->auxil.img.wd,
			XtNimage,&image,
			NULL);
		XDestroyImage(image);
		get_image_cmap(ObjectToVns(obj),obj,&obj->auxil.img.img,&image,&cmap);
		XtVaSetValues(obj->auxil.img.wd,
			XtNimage,image,
			XtNcolormap,cmap,
			NULL);
	}
	VnsPropObject(obj);
}

/*ARGSUSED*/
static
moved_off_page(w,ol,mo)
	Widget w;
	VnsObject *ol;
	MovedOff *mo;
{
	Widget new_wd = XtWindowToWidget(XtDisplay(w),mo->w);
	VnsContext *vns = ObjectToVns(ol);

	if (new_wd != NULL)
	{
		Widget shell = new_wd;
		VnsPage *pl = vns->first_page;
		while(!XtIsShell(shell))
		{
			shell = XtParent(shell);
		}
		while(pl != NULL && pl->pwd != shell)
		{
			pl = pl->npage;
		}
		if (pl != NULL)
		{
			VnsNotebook *nl;
			Object_Block block;
			VnsObject *new_o;
			Window child;
			int dest_x,dest_y;

			/* check notebook access */
			nl = PageToNote(pl);
			if (!nl->info.can_write) return;

			vns_watch_on(PageToVns(pl));

			block.info = ol->info;
			XTranslateCoordinates(XtDisplay(w),mo->w,XtWindow(pl->comp),
				mo->x,mo->y,&dest_x,&dest_y,&child);
			VwGetCoordinates(pl->comp,dest_x,dest_y,&block.info.x,&block.info.y);
			block.oid = srv_create_object(PageToSrv(pl),nl->nid,pl->pid,&block.info);
			switch(block.info.type)
			{
				case OT_TXT:
					XtVaGetValues(ol->auxil.txt.wd,
						XtNstring,&block.txt,
						NULL);
					srv_set_object_text(PageToSrv(pl),nl->nid,pl->pid,block.oid,block.txt);
					break;
				case OT_IMAGE:
					copy_img(&ol->auxil.img.img,&block.img);
					srv_set_object_img(PageToSrv(pl),nl->nid,pl->pid,block.oid,&block.img);
					break;
			}
			new_o = add_object2(pl,&block);
			select_object(new_o);
			update_page_scrollbars(pl);
			if (!mo->shift_pressed)
			{
				int oid = ol->oid;
				VnsPage *page = ObjectToPage(ol);
				VnsNotebook *note = PageToNote(page);

				del_object(ol);
				srv_delete_object(NoteToSrv(note),note->nid,page->pid,oid);
			}

			vns_watch_off(PageToVns(pl));
		}
	}
}

/*
 * Selects an Object
 */
static
void
object_select(obj)
	VnsObject *obj;
{
	VnsNotebook *nl;
	VnsPage *pl;

	if (obj == NULL) return;

	/* can't select an object that is allready selected */
	if (obj->selected) return;

	pl = ObjectToPage(obj);
	nl = ObjectToNote(obj);

	/* set the object's select flag to True */
	obj->selected = TRUE;

	/* increment the counter for number of page objects selected */
	++pl->nselected;

	/* manage the resize widget associated with the object */
	XtVaSetValues(obj->resize,
		XtNsensitive,nl->info.can_write,
		NULL);
	XtManageChild(obj->resize);

	/* raise the resize widget and then the object */
	XRaiseWindow(XtDisplay(obj->resize),XtWindow(obj->resize));
	XRaiseWindow(XtDisplay(obj->owd),XtWindow(obj->owd));

	/* update property manager for selected object */
	VnsPropObject(obj);

	/* set the currently selected object for the page */
	pl->selected_object = obj;

	/* depending on the object type, do more stuff */
	switch(obj->info.type)
	{
		case OT_TXT:
			XtVaSetValues(obj->auxil.txt.wd,
				XtNreadOnly,!nl->info.can_write,
				NULL);
			XtSetKeyboardFocus(pl->pwd,obj->auxil.txt.wd);
			break;
		case OT_ACTION_LINK:
			XtVaSetValues(obj->auxil.link.wd,
				XtNreadOnly,!nl->info.can_write,
				NULL);
			XtSetKeyboardFocus(pl->pwd,obj->auxil.link.wd);
			break;
		case OT_LINK:
			XtVaSetValues(obj->auxil.link.wd,
				XtNreadOnly,!nl->info.can_write,
				NULL);
			XtSetKeyboardFocus(pl->pwd,obj->auxil.link.wd);
			vns_activate_link(obj);
			break;
	}
}

/*
 * Deselects an Object
 */
static
void
object_deselect(obj)
	VnsObject *obj;
{
	VnsNotebook *nl;
	VnsPage *pl;

	if (obj == NULL) return;

	/* can't deselect an object that isn't selected */
	if (!obj->selected) return;

	/* decrement the number of objects selected for the parent page */
	nl = ObjectToNote(obj);
	pl = ObjectToPage(obj);
	--pl->nselected;

	/* set the object's select flag to False */
	obj->selected = FALSE;

	/* unmanage the resize widget for the object */
	XtUnmanageChild(obj->resize);

	/* the objects are now restacked to the appropriate levels */
	stack_objects(pl);

	/* depending on the object, save the data */
	switch(obj->info.type)
	{
		case OT_TXT:
		{
			char *s;
			XtVaGetValues(obj->auxil.txt.wd,
				XtNstring,&s,
				NULL);
			if (nl->info.can_write)
			{
				srv_set_object_text(ObjectToSrv(obj),nl->nid,pl->pid,obj->oid,s);
			}
			XtVaSetValues(obj->auxil.txt.wd,
				XtNreadOnly,True,
				NULL);
			XtSetKeyboardFocus(pl->pwd,pl->comp);
			break;
		}
		case OT_LINK:
		{
			char *s;
			XtVaGetValues(obj->auxil.link.wd,
				XtNstring,&s,
				NULL);
			strcpy(obj->info.data.l_info.title,s);
			if (nl->info.can_write)
			{
				srv_modify_object(ObjectToSrv(obj),nl->nid,
					pl->pid,obj->oid,&obj->info);
			}
			XtVaSetValues(obj->auxil.link.wd,
				XtNreadOnly,True,
				NULL);
			XtSetKeyboardFocus(pl->pwd,pl->comp);

			/* deactivate the link-here toggles on all pages */
			vns_deactivate_link(obj);
			break;
		}
		case OT_ACTION_LINK:
		{
			char *s;
			XtVaGetValues(obj->auxil.link.wd,
				XtNstring,&s,
				NULL);
			strcpy(obj->info.data.al_info.title,s);
			if (nl->info.can_write)
			{
				srv_modify_object(ObjectToSrv(obj),nl->nid,
					pl->pid,obj->oid,&obj->info);
			}
			XtVaSetValues(obj->auxil.link.wd,
				XtNreadOnly,True,
				NULL);
			XtSetKeyboardFocus(pl->pwd,pl->comp);
			break;
		}
	}
}

page_deselect(pl)
	VnsPage *pl;
{
	if (pl == NULL) return;

	if (pl->nselected <= 0)
	{
		pl->selected_object = NULL;
		pl->nselected = 0;
		return;
	}
	else if (pl->nselected == 1)
	{
		object_deselect(pl->selected_object);
		pl->selected_object = NULL;
		pl->nselected = 0;
		return;
	}
	else
	{
		VnsObject *obj = pl->first_object;
		while(obj != NULL)
		{
			object_deselect(obj);
			obj = obj->next;
		}
		pl->nselected = 0;
		pl->selected_object = NULL;
		return;
	}
}

/*
 * deselect all objects on all pages (i.e. everything).
 */
deselect_all(vns)
	VnsContext *vns;
{
	VnsPage *page = vns->first_page;

	/* deselect all objects on each page */
	while(page != NULL)
	{
		page_deselect(page);
		back_deselect(page->comp);
		page = page->npage;
	}
}

/*
 * Main driver for object selection
 */
static
void
select_object2(obj,force,shift)
	VnsObject *obj;
	int force;
	int shift;
{
	VnsPage *pl = ObjectToPage(obj);

	/* if force is True, then the object allways gets selected */
	if (force)
	{
		if (obj->selected)
		{
			return;
		}
		else
		{
			/* if first object on the poge, deselect all objects on other pages */
			if (pl->nselected <= 0)
			{
				deselect_all(ObjectToVns(obj));
			}
			object_select(obj);
			return;
		}
	}
	else
	{
		if (shift)
		{
			/* for shift, we toggle object selection the same as without shift */
			if (obj->selected)
			{
				object_deselect(obj);
				return;
			}
			else
			{
				/* if first object on the poge, deselect all objects on other pages */
				if (pl->nselected <= 0)
				{
					deselect_all(ObjectToVns(obj));
				}
				object_select(obj);
			}
		}
		else
		{
/*
			if (!obj->selected)
			{
				deselect_all(ObjectToVns(obj));
				object_select(obj);
			}
*/
			if (obj->selected)
			{
				object_deselect(obj);
				return;
			}
			else
			{
				deselect_all(ObjectToVns(obj));
				object_select(obj);
			}
		}
	}
}

/*
 * select a specific object.
 */
select_object(obj)
	VnsObject *obj;
{
	select_object2(obj,FALSE,FALSE);
}

/*
 * select all of the objects on a page.
 */
select_page(pl)
	VnsPage *pl;
{
	VnsObject *obj;

	if (pl == NULL) return;

	obj = pl->first_object;
	while(obj != NULL)
	{
		select_object2(obj,TRUE,FALSE);
		obj = obj->next;
	}
}

/*
 * Callback for button events on all objects (looking for button 2 activations)
 */
/*ARGSUSED*/
static
void
middle_mouse(wd,ol,event)
	Widget wd;
	VnsObject *ol;
	XButtonEvent *event;
{
	if (event->button == Button2)
	{
		if (event->state & ShiftMask)
		{
			select_object2(ol,FALSE,TRUE);
		}
		else
		{
			select_object2(ol,FALSE,FALSE);
		}
	}
}

get_image_cmap(vns,new,img,image,cmap)
	VnsContext *vns;
	VnsObject *new;
	Rast_Img *img;
	Colormap *cmap;
	XImage **image;
{
	Visual *vis = DefaultVisual(vns->dpy,DefaultScreen(vns->dpy));
	*cmap = DefaultColormap(vns->dpy,DefaultScreen(vns->dpy));
	if (vis->class == StaticGray && img->depth > 1)
	{
		VtCreateDitheredPixmapFromImg(vns->dpy,
			img,
			image,(int)new->info.width,(int)new->info.height);
	}
	else if (new->info.data.i_info.create_cmap &&
		img->depth == 8 &&
		(!vis->red_mask || !vis->green_mask || !vis->blue_mask) &&
		vis->map_entries >= (img->cmap_type == CMAP_RGB ? img->cmap_data.rgb.map_size : 256))
	{
		VtCreateCmapPixmapFromImg(vns->dpy,
			img,
			image,
			cmap,(int)new->info.width,(int)new->info.height);
	}
	else
	{
		VtCreateSubstPixmapFromImg(vns->dpy,
			img,
			image,(int)new->info.width,(int)new->info.height);
	}
}

VnsObject *
add_object2(pl,block)
	VnsPage *pl;
	Object_Block *block;
{
	VnsObject **ol = VnsFindObject(pl,block->oid);
	if (*ol == NULL)
	{
		VnsObject *new = (VnsObject *)malloc(sizeof *new);

		new->info = block->info;
		new->parent = pl;
		new->selected = FALSE;
		switch(new->info.type)
		{
			case OT_TXT:
			{
				Widget txt_frm;
				XFontStruct *font = alloc_named_font(PageToVns(pl),new->info.data.t_info.font);
				unsigned long col = VnsAllocNamedColor(PageToVns(pl),new->info.data.t_info.color);
				char *s = block->txt;
				char name[50];

				check_background(XtDisplay(pl->comp),pl->col,&col);

				txt_frm = XtVaCreateManagedWidget("TextObject",vListWidgetClass,pl->comp,
					XtNborderWidth,0,
					XtNx,new->info.x,
					XtNy,new->info.y,
					XtNbackground,pl->col,
					XtNwidth,new->info.width,
					XtNheight,new->info.height,
					XtNnumRows,1,
					XtNallowResizeX,False,
					XtNallowResizeY,False,
					XtNgroup,True,
					NULL);
				sprintf(name,"TextObject%d",new->oid);
				new->auxil.txt.wd = XtVaCreateWidget(name,
					vnsTextWidgetClass,txt_frm,
					XtNstring,s,
					XtNreadOnly,TRUE,
					XtNborderWidth,0,
					XtNforeground,col,
					XtNbackground,pl->col,
					XtNfont,font,
					XtNstretchHoriz,True,
					XtNstretchVert,True,
					NULL);
				XtAddEventHandler(new->auxil.txt.wd,ButtonPressMask,False,
					middle_mouse,(XtPointer)new);
				XtAddEventHandler(txt_frm,ButtonPressMask,False,
					middle_mouse,(XtPointer)new);
				VtManageChildren(txt_frm);
				new->owd = txt_frm;
				break;
			}
			case OT_LINK:
			{
				char label[50];
				Widget lnk_frm, link_bits, link_label;
				unsigned long col = VnsAllocNamedColor(PageToVns(pl),new->info.data.l_info.color);
				XFontStruct *font = alloc_named_font(PageToVns(pl),new->info.data.l_info.font);
				int valid;

				check_background(XtDisplay(pl->comp),pl->col,&col);

				/* check if destination notebook and page are valid */
				valid = srv_validate_page(PageToSrv(pl),
							new->info.data.l_info.dest_note,
							new->info.data.l_info.dest_page);

				lnk_frm = XtVaCreateManagedWidget("LinkObject",vListWidgetClass,pl->comp,
					XtNborderWidth,0,
					XtNx,new->info.x,
					XtNy,new->info.y,
					XtNbackground,pl->col,
					XtNwidth,new->info.width,
					XtNheight,new->info.height,
					XtNnumRows,1,
					XtNallowResizeX,False,
					XtNallowResizeY,False,
					XtNgroup,True,
					NULL);

				if (valid == 1)
				{
					link_bits = XtVaCreateWidget("LinkBits",
						xmPushButtonWidgetClass,lnk_frm,
						XmNmarginWidth,0,
						XmNmarginHeight,0,
						XmNshadowThickness,0,
						XmNlabelType,XmPIXMAP,
						XmNlabelPixmap,link_bitmap,
						XmNborderWidth,0,
						XmNforeground,col,
						XmNbackground,pl->col,
						NULL);
				}
				else if (valid == 2)
				{
					link_bits = XtVaCreateWidget("LinkBits",
						xmPushButtonWidgetClass,lnk_frm,
						XmNmarginWidth,0,
						XmNmarginHeight,0,
						XmNshadowThickness,0,
						XmNlabelType,XmPIXMAP,
						XmNlabelPixmap,dlink_bitmap,
						XmNborderWidth,0,
						XmNforeground,col,
						XmNbackground,pl->col,
						NULL);
				}
				else if (valid == 3)
				{
					link_bits = XtVaCreateWidget("LinkBits",
						xmPushButtonWidgetClass,lnk_frm,
						XmNmarginWidth,0,
						XmNmarginHeight,0,
						XmNshadowThickness,0,
						XmNlabelType,XmPIXMAP,
						XmNlabelPixmap,badlink_bitmap,
						XmNborderWidth,0,
						XmNforeground,col,
						XmNbackground,pl->col,
						NULL);
				}

				sprintf(label,"LinkLabel%d",new->oid);
				link_label = XtVaCreateWidget(label,vnsTextWidgetClass,lnk_frm,
					XtNstring,new->info.data.l_info.title,
					XtNreadOnly,TRUE,
					XtNborderWidth,0,
					XtNforeground,col,
					XtNbackground,pl->col,
					XtNfont,font,
					XtNstretchHoriz,True,
					XtNstretchVert,True,
					NULL);

				XtAddEventHandler(link_bits,ButtonPressMask,False,select_link,(XtPointer)new);
				XtAddEventHandler(lnk_frm,ButtonPressMask,False,middle_mouse,(XtPointer)new);
				XtAddEventHandler(link_bits,ButtonPressMask,False,middle_mouse,(XtPointer)new);
				XtAddEventHandler(link_label,ButtonPressMask,False,middle_mouse,(XtPointer)new);
				VtManageChildren(lnk_frm);
				new->owd = lnk_frm;
				new->auxil.link.wd = link_label;
				new->auxil.link.button = link_bits;
				break;
			}
			case OT_ACTION_LINK:
			{
				Widget lnk_frm, link_bits, link_label;
				unsigned long col = VnsAllocNamedColor(PageToVns(pl),new->info.data.al_info.color);
				XFontStruct *font = alloc_named_font(PageToVns(pl),new->info.data.al_info.font);
				int len = strlen(new->info.data.al_info.command);

				check_background(XtDisplay(pl->comp),pl->col,&col);

				lnk_frm = XtVaCreateManagedWidget("ActionLinkObject",vListWidgetClass,pl->comp,
					XtNborderWidth,0,
					XtNx,new->info.x,
					XtNy,new->info.y,
					XtNforeground,col,
					XtNbackground,pl->col,
					XtNwidth,new->info.width,
					XtNheight,new->info.height,
					XtNnumRows,1,
					XtNallowResizeX,False,
					XtNallowResizeY,False,
					XtNgroup,True,
					NULL);
				link_bits = XtVaCreateWidget("ActionLinkBits",
					xmPushButtonWidgetClass,lnk_frm,
					XmNmarginWidth,0,
					XmNmarginHeight,0,
					XmNshadowThickness,0,
					XmNlabelType,XmPIXMAP,
					XmNlabelPixmap,(len > 0) ? action_link_bitmap : qmark_bitmap,
					XmNborderWidth,0,
					XmNforeground,col,
					XmNbackground,pl->col,
					NULL);
				link_label = XtVaCreateWidget("ActionLinkLab",vnsTextWidgetClass,lnk_frm,
					XtNstring,new->info.data.al_info.title,
					XtNreadOnly,TRUE,
					XtNborderWidth,0,
					XtNforeground,col,
					XtNbackground,pl->col,
					XtNfont,font,
					XtNstretchHoriz,True,
					XtNstretchVert,True,
					NULL);

				XtAddCallback(link_bits,XmNactivateCallback,select_action_link,(XtPointer)new);
				XtAddEventHandler(lnk_frm,ButtonPressMask,False,middle_mouse,(XtPointer)new);
				XtAddEventHandler(link_bits,ButtonPressMask,False,middle_mouse,(XtPointer)new);
				XtAddEventHandler(link_label,ButtonPressMask,False,middle_mouse,(XtPointer)new);
				VtManageChildren(lnk_frm);
				new->owd = lnk_frm;
				new->auxil.link.wd = link_label;
				new->auxil.link.button = link_bits;
				break;
			}
			case OT_IMAGE:
			{
				XImage *image;
				Colormap cmap;
				get_image_cmap(PageToVns(pl),new,&block->img,&image,&cmap);
				new->auxil.img.img = block->img;
				new->owd = new->auxil.img.wd = XtVaCreateManagedWidget("ImageObject",
					JselRasterWidgetClass,pl->comp,
					XtNx,new->info.x,
					XtNy,new->info.y,
					XtNimage,image,
					XtNwidth,block->img.width,
					XtNheight,block->img.height,
					XtNborderWidth,0,
					XtNcolormap,cmap,
					XtNbackground,pl->col,
					XtNwidth,new->info.width,
					XtNheight,new->info.height,
					XtNgroup,True,
					NULL);
				XtAddEventHandler(new->auxil.img.wd,ButtonPressMask,False,
					middle_mouse,(XtPointer)new);
				break;
			}
		}

		/* create resize widget and the new object as a target */
		new->resize = XtVaCreateWidget("ResizeObject",vnsResizeWidgetClass,pl->comp,
			EtNtargetWidget,new->owd,
			XtNsensitive,False,
			NULL);
		XtAddCallback(new->resize,EtNreConfigured,reconfigured,(XtPointer)new);
		XtAddCallback(new->resize,EtNmovedOffParent,moved_off_page,(XtPointer)new);

		/* manage the objects childrent */
		VtManageChildren(new->owd);
		new->oid = block->oid;
		new->next = NULL;
		*ol = new;
	}
	return *ol;
}

VnsObject *
add_object(pl,oid)
	VnsPage *pl;
	int oid;
{
	VnsNotebook *nl = PageToNote(pl);
	VnsObject **ol = VnsFindObject(pl,oid);

	if (*ol == NULL)
	{
		VnsObject *object;
		Object_Block block;
		block.oid = oid;
		srv_inq_object(PageToSrv(pl),nl->nid,pl->pid,oid,&block.info);
		switch(block.info.type)
		{
			case OT_TXT:
				block.txt = srv_get_object_text(PageToSrv(pl),nl->nid,pl->pid,oid);
				break;
			case OT_IMAGE:
				srv_get_object_img(PageToSrv(pl),nl->nid,pl->pid,oid,&block.img);
				break;
		}
		object = add_object2(pl,&block);
		switch(block.info.type)
		{
			case OT_TXT:
				free(block.txt);
				break;
		}
		/* stack the page objects */
		stack_objects(pl);

		return object;
	}
	else
	{
		return *ol;
	}
}

change_object(ol)
	VnsObject *ol;
{
	VnsContext *vns = ObjectToVns(ol);
	VnsPage *pl = ObjectToPage(ol);
	VnsNotebook *nl = ObjectToNote(ol);

	srv_inq_object(ObjectToSrv(ol),nl->nid,pl->pid,ol->oid,&ol->info);

	/*
	 * this function modifies the size and location of the object on the
	 * page and should be given a more meaningful name so we know that it
	 * is a function contained in the VnsBackground widget.
	 */
	change_loc(ol->owd,
		(int)ol->info.x,
		(int)ol->info.y,
		(int)ol->info.width,
		(int)ol->info.height);

	switch(ol->info.type)
	{
		case OT_TXT:
		{
			XFontStruct *font = alloc_named_font(vns,ol->info.data.t_info.font);
			unsigned long col = VnsAllocNamedColor(vns,ol->info.data.t_info.color);
			check_background(XtDisplay(pl->comp),pl->col,&col);
			XtVaSetValues(ol->auxil.txt.wd,
				XtNfont,font,
				NULL);
			VtSetForeColor(ol->owd,col);
			break;
		}
		case OT_LINK:
		{
			XFontStruct *font = alloc_named_font(vns,ol->info.data.l_info.font);
			unsigned long col = VnsAllocNamedColor(vns,ol->info.data.l_info.color);
			int valid;

			check_background(XtDisplay(pl->comp),pl->col,&col);

			/* check if destination notebook and page are valid */
			valid = srv_validate_page(ObjectToSrv(ol),
					ol->info.data.l_info.dest_note,
					ol->info.data.l_info.dest_page);

			if (valid == 1)
			{
				XtVaSetValues(ol->auxil.link.button,
					XmNlabelPixmap,link_bitmap,
					NULL);
			}
			else if (valid == 2)
			{
				XtVaSetValues(ol->auxil.link.button,
					XmNlabelPixmap,dlink_bitmap,
					NULL);
			}
			else if (valid == 3)
			{
				XtVaSetValues(ol->auxil.link.button,
					XmNlabelPixmap,badlink_bitmap,
					NULL);
			}

			/* modify the link label */
			XtVaSetValues(ol->auxil.link.wd,
				XtNfont,font,
				XtNstring,ol->info.data.l_info.title,
				NULL);
			VtSetForeColor(ol->owd,col);
			break;
		}
		case OT_ACTION_LINK:
		{
			XFontStruct *font = alloc_named_font(vns,ol->info.data.al_info.font);
			unsigned long col = VnsAllocNamedColor(vns,ol->info.data.al_info.color);
			int len = strlen(ol->info.data.al_info.command);

			check_background(XtDisplay(pl->comp),pl->col,&col);

			if (len > 0)
			{
				XtVaSetValues(ol->auxil.link.button,
					XmNlabelPixmap,action_link_bitmap,
					NULL);
			}
			else
			{
				XtVaSetValues(ol->auxil.link.button,
					XmNlabelPixmap,qmark_bitmap,
					NULL);
			}

			XtVaSetValues(ol->auxil.link.wd,
				XtNfont,font,
				XtNstring,ol->info.data.al_info.title,
				NULL);
			VtSetForeColor(ol->owd,col);
			break;
		}
		case OT_IMAGE:
		{
			XImage *image;
			Colormap cmap;
			XtVaGetValues(ol->auxil.img.wd,
				XtNimage,&image,
				NULL);
			XDestroyImage(image);
			get_image_cmap(ObjectToVns(ol),ol,&ol->auxil.img.img,&image,&cmap);
			XtVaSetValues(ol->auxil.img.wd,
				XtNimage,image,
				XtNcolormap,cmap,
				NULL);
			break;
		}
	}
}

/*
 * delete a oobject
 */
del_object(obj)
	VnsObject *obj;
{
	VnsPage *pl = ObjectToPage(obj);
	VnsObject **p = VnsFindObject(pl,obj->oid);

	if (*p != NULL)
	{
		XImage *image;

		/* deselect the object */
		object_deselect(obj);

		/* inform the property manager */
		VnsPropResetObject(obj);

		switch(obj->info.type)
		{
			case OT_IMAGE:
				XtVaGetValues(obj->auxil.img.wd,
					XtNimage,&image,
					NULL);
				break;
		}

		/* destroy the object widget */
		XtDestroyWidget(obj->owd);

		/* destroy the resize widget */
		XtDestroyWidget(obj->resize);

		switch(obj->info.type)
		{
			case OT_IMAGE:
				destroy_img(&obj->auxil.img.img);
				XDestroyImage(image);
				break;
		}

		*p = obj->next;
		free((char *)obj);
	}
}
