/* This file includes basic Paige stuff that always needs to be linked in. You
cannot omit this file from your app!   */

/* Modified Dec 22 1994 for the use of volatile in PG_TRY-PG_CATCH by TR Shaw, OITC */

#include "Paige.h"

#ifdef MAC_PLATFORM
#pragma segment pgbasic3
#endif

#include "pgExceps.h"       
#include "pgText.h"
#include "pgDefStl.h"
#include "pgDefPar.h"
#include "pgSelect.h"
#include "pgEdit.h"
#include "pgShapes.h"
#include "pgUtils.h"
#include "machine.h"
#include "defprocs.h"
#include "pgBasics.h"
#include "pgTxtWid.h"
#include "pgSubref.h"
#include "pgLists.h"

static shape_ref copy_shape_param (pg_globals_ptr globals, shape_ref shape_param);
static long free_cached_blocks (paige_rec_ptr pg, memory_ref ref_mask, long needed_free);


/* DEFAULT "MERGE TEXT" PROC. By default, this simply returns FALSE which
creates a non-effect for merging.  */

PG_PASCAL (pg_boolean) pgMergeProc (paige_rec_ptr pg, style_info_ptr style, pg_char_ptr text_data,
		pg_short_t length, text_ref merged_data, long ref_con)
{
#ifdef MAC_PLATFORM
#pragma unused (pg, style, text_data, length, merged_data, ref_con)
#endif
	return	FALSE;
}


/* DEFAULT "UNDO" PROC which gives the host program a chance to modify and/or
perform actions for custom undo's. BY default this does nothing (since standard
undo's are handled in pgClipBd.c). */

PG_PASCAL (void) pgEnhanceUndo (paige_rec_ptr pg, pg_undo_ptr undo_rec,
		void PG_FAR *insert_ref, short action_to_take)
{
#ifdef MAC_PLATFORM
#pragma unused (pg, undo_rec, insert_ref, action_to_take)
#endif
}

/* pgCacheFree is the function (for 2.0) which frees text_blocks that are cached but not
changed. This occurs before memory_refs are purged. */

PG_PASCAL (long) pgCacheFree (pgm_globals_ptr mem_globals, memory_ref dont_free, long desired_free)
{
	pg_ref PG_FAR	*paige_refs;
	paige_rec_ptr	pg_rec;
	long			num_refs;
	long			freed_memory = 0;

	if (mem_globals->freemem_info) {
		
		num_refs = GetMemorySize(mem_globals->freemem_info);
		paige_refs = UseMemory(mem_globals->freemem_info);
		
		while (num_refs) {
			
			if (*paige_refs && (*paige_refs != dont_free)) {
				
				pg_rec = UseMemory(*paige_refs);
				
				if (pg_rec->cache_file)
					freed_memory += free_cached_blocks(pg_rec, dont_free, desired_free - freed_memory);

				UnuseMemory(*paige_refs);
				
				if (freed_memory >= desired_free)
					break;
			}

			++paige_refs;
			--num_refs;
		}
		
		UnuseMemory(mem_globals->freemem_info);
	}

	return		freed_memory;
}


/* pgExtendProc gets called after a new pg_ref has been created, and also
when it has been disposed. The default function does nothing. */

PG_PASCAL (void) pgExtendProc (void PG_FAR *param, short verb)
{

}



/* pgWaitProc is the standard "wait-process" function, which does nothing.  */

PG_PASCAL (void) pgWaitProc (paige_rec_ptr pg, short wait_verb, long progress_ctr,
		long completion_ctr)
{
#ifdef MAC_PLATFORM
#pragma unused (pg, wait_verb, progress_ctr, completion_ctr)
#endif
}


/* pgDrawScrollProc is the default pre and post-draw for scrolling. It does nothing
by default. When it gets called the clipping is set to the scrolled area. */

PG_PASCAL (void) pgDrawScrollProc (paige_rec_ptr pg, shape_ref update_rgn,
		co_ordinate_ptr scroll_pos, pg_boolean post_call)
{
#ifdef MAC_PLATFORM
#pragma unused (pg, update_rgn, scroll_pos, post_call)
#endif
}


/* pgModifyContainerProc is the default container-modify function, which does
nothing by default. */

PG_PASCAL (void) pgModifyContainerProc (paige_rec_ptr pg, pg_short_t container_num,
		rectangle_ptr container, pg_scale_ptr scaling, co_ordinate_ptr screen_extra,
		short verb, void PG_FAR *misc_info)
{
#ifdef MAC_PLATFORM
#pragma unused (container_num, misc_info)
#endif

	if (verb == erase_rect_verb) {
		
		if (pgEmptyRect(container))
			return;

		if ((pg->flags & BITMAP_ERASE_BIT) && !pg->globals->offscreen_enable) {
			rectangle		rect_to_erase, vis_rect, scaled_vis_rect, actual_rect;
			co_ordinate		not_used, not_scrolled;
			
			pgScaleRectToRect(scaling, container, &rect_to_erase, screen_extra);
			pgShapeBounds(pg->vis_area, &vis_rect);
			
			if (screen_extra) {
				
				not_scrolled = *screen_extra;
				pgAddPt(&pg->scroll_pos, &not_scrolled);
			}
			else
				not_scrolled.h = not_scrolled.v = 0;
			
			pg->port.scale.scale = -pg->port.scale.scale;
			pgScaleRectToRect(&pg->port.scale, &vis_rect, &scaled_vis_rect, &not_scrolled);
			pg->port.scale.scale = -pg->port.scale.scale;

			if (pgSectRect(&rect_to_erase, &scaled_vis_rect, &rect_to_erase)) {
			
				pgPrepareOffscreen(pg, &rect_to_erase, &actual_rect, &not_used, 0, NULL, bits_copy);
	
				for (;;) {
					
					if (pgFinishOffscreen(pg, 0, NULL, &not_used, &actual_rect, bits_copy))
						break;
				}
			}
		}
		else
			pgEraseRect(pg->globals, container, scaling, screen_extra);
	}
}


/* pgDrawPageProc is the default "page" drawing for scrolling. It does nothing
by default.  */

PG_PASCAL (void) pgDrawPageProc (paige_rec_ptr pg, shape_ptr page_shape,
		pg_short_t r_qty, pg_short_t page_num, co_ordinate_ptr vis_offset,
		short draw_mode_used, short call_order)
{
#ifdef MAC_PLATFORM
#pragma unused (pg, page_shape, r_qty, page_num, vis_offset, call_order, draw_mode_used)
#endif
}


/* pgSetAuthor sets the app signature */

PG_PASCAL (void) pgSetAuthor (pg_ref pg, long author)
{
	paige_rec_ptr		pg_rec;
	
	pg_rec = UseMemory(pg);
	pg_rec->author = author;
	UnuseMemory(pg);
}

/* pgGetAuthor returns the app signature */

PG_PASCAL (long) pgGetAuthor (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	long				author;
	
	pg_rec = UseMemory(pg);
	author = pg_rec->author;
	UnuseMemory(pg);
	
	return	author;
}


/* pgSetLockID sets an app-chosen long ID for "locking." If the current
ID is non-zero and ID param is non-zero, function result is FALSE if they
don't match and the ID not set. Otherwise, ID is set and result is TRUE. */

PG_PASCAL (pg_boolean) pgSetLockID (pg_ref pg, long ID)
{
	paige_rec_ptr		pg_rec;
	long				cur_id;

	pg_rec = UseMemory(pg);
	
	cur_id = pg_rec->lock_id;

	if ((!ID) || (!cur_id))
		pg_rec->lock_id = cur_id = ID;

	UnuseMemory(pg);
	
	return	((!ID) || (cur_id == ID));
}


/* pgGetLockID returns the current lock ID  */

PG_PASCAL (long) pgGetLockID (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	long				result;
	
	pg_rec = UseMemory(pg);
	result = pg_rec->lock_id;
	UnuseMemory(pg);
	
	return	result;
}


/* pgNew creates a new, empty Paige object and returns the pg_ref */

PG_PASCAL (pg_ref) pgNew (const pg_globals_ptr globals, const generic_var def_device,
		shape_ref vis_area, shape_ref page_area, shape_ref exclude_area,
		long attributes)
{
	register paige_rec_ptr		pg_rec;
	rectangle_ptr				vis_ptr;
	style_info_ptr				first_style;
	par_info_ptr				first_par;
	font_info_ptr				first_font;
	style_run_ptr				first_run;
#ifdef PG_OITC
	pg_list_rec					list_rec;
#endif

#ifndef PG_VOLATILE
	volatile pg_ref				pg = MEM_NULL;
#else
	pg_ref 						pg;

	PGVolatile(pg);
	
	pg = MEM_NULL;
#endif

	pg_rec = NULL;

	PG_TRY(globals->mem_globals) {

		++globals->mem_globals->next_mem_id;
		globals->mem_globals->current_id = globals->mem_globals->next_mem_id;
	
		pg = MemoryAllocClear(globals->mem_globals, sizeof(paige_rec), 1, 0);
		pg_rec = UseMemory(pg);
		pg_rec->mem_id = globals->mem_globals->current_id;
	
		pg_rec->globals = globals;
		pg_rec->version = PAIGE_VERSION;
		pg_rec->platform = PAIGE_PLATFORM;
		pg_rec->pg_type = MAIN_DOCUMENT;
		pg_rec->doc_pg = pg;
		pg_rec->flags = attributes & (~NO_DEVICE_BIT);
		pg_rec->flags2 = INITIAL_TEXT_FLAGS;

		if (pg_rec->flags & LINE_EDITOR_BIT)
			pg_rec->flags |= (NO_WRAP_BIT | NO_LF_BIT);

		pg_rec->scroll_align_h = pg_rec->scroll_align_v = -1;
		pg_rec->doc_info.num_pages = 1;
		pg_rec->text_direction = pgSystemDirection(globals);

		pg_rec->bk_color = globals->def_bk_color;
		pgBlockMove(&globals->def_hooks, &pg_rec->procs, sizeof(pg_hooks));
		
		pg_rec->autoscroll_mode = bits_emulate_or;

		if (!def_device)
			pgInitDefaultDevice(globals, &pg_rec->port);
		else {
			
			if (def_device == USE_NO_DEVICE) {
				
				pgBlockMove(&globals->offscreen_port, &pg_rec->port, sizeof(graf_device));
				pg_rec->flags |= NO_DEVICE_BIT;
				pg_rec->port.resolution = pgDeviceResolution(&pg_rec->port);
			}
			else {
			
				pgInitDevice(globals, def_device, 0, &pg_rec->port);
			}
		}

		pg_rec->resolution = pg_rec->port.resolution;
		pg_rec->myself = pg;

		pgScaleGrafDevice(pg_rec);

		pg_rec->select = MemoryAllocClear(globals->mem_globals, sizeof(t_select),
				MINIMUM_SELECT_MEMSIZE, 4);

		pg_rec->vis_area = copy_shape_param(globals, vis_area);
		pg_rec->wrap_area = copy_shape_param(globals, page_area);
		pg_rec->exclude_area = copy_shape_param(globals, exclude_area);
		
		vis_ptr = UseMemory(pg_rec->vis_area);
		pg_rec->base_vis_origin = vis_ptr->top_left;
		UnuseMemory(pg_rec->vis_area);

		pg_rec->t_blocks = MemoryAlloc(globals->mem_globals, sizeof(text_block), 1, 1);
		pg_rec->tlevel_blocks = pg_rec->t_blocks;
		pgInitTextblock(pg_rec, 0, 0, UseMemory(pg_rec->t_blocks), FALSE);
		UnuseMemory(pg_rec->t_blocks);
	
		pg_rec->hilite_rgn = pgRectToShape(globals->mem_globals, NULL);
		pg_rec->temp_rgn = pgRectToShape(globals->mem_globals, NULL);
		pg_rec->t_formats = MemoryAllocClear(globals->mem_globals, sizeof(style_info), 1, 1);
	
		pg_rec->par_formats = MemoryAllocClear(globals->mem_globals, sizeof(par_info), 1, 1);
		pg_rec->fonts = MemoryAllocClear(globals->mem_globals, sizeof(font_info), 1, 1);
		pg_rec->t_style_run = MemoryAllocClear(globals->mem_globals, sizeof(style_run), 2, 4);
		pg_rec->par_style_run = MemoryAllocClear(globals->mem_globals, sizeof(style_run), 2, 4);
		pg_rec->par_exclusions = MemoryAllocClear(globals->mem_globals, sizeof(style_run), 0, 2);
		pg_rec->key_buffer = MemoryAlloc(globals->mem_globals, sizeof(pg_char), 0, 16);
		pg_rec->containers = MemoryAlloc(globals->mem_globals, sizeof(long), 0, 4);
		pg_rec->exclusions = MemoryAlloc(globals->mem_globals, sizeof(long), 0, 4);
		pg_rec->buf_special = MemoryAlloc(globals->mem_globals, sizeof(pg_char), 0, 24);
		pg_rec->named_styles = MemoryAlloc(globals->mem_globals, sizeof(named_stylesheet), 0, 0);

#ifdef PG_SUBSETS
		pg_rec->subref_stack = MemoryAlloc(globals->mem_globals, sizeof(paige_sub_rec), 0, 2);
#endif

		first_font = UseMemory(pg_rec->fonts);
		pgBlockMove(&globals->def_font, first_font, sizeof(font_info));
			
		first_style = UseMemory(pg_rec->t_formats);
		pgBlockMove(&globals->def_style, first_style, sizeof(style_info));

		pg_rec->procs.font_proc(pg_rec, first_font);
		first_style->procs.init(pg_rec, first_style, first_font);
		first_style->procs.duplicate(MEM_NULL, pg_rec, pg_new_reason,
			pg_rec->t_formats, first_style);

		first_style->used_ctr = 2;	// Gar change to force default font to never delete
	
		UnuseMemory(pg_rec->fonts);
		UnuseMemory(pg_rec->t_formats);
		
		first_par = UseMemory(pg_rec->par_formats);
		pgBlockMove(&globals->def_par, first_par, sizeof(par_info));
		
		first_par->procs.duplicate(MEM_NULL, pg_rec, pg_new_reason,
			pg_rec->par_formats, first_par);
	
		first_par->used_ctr = 2;
		UnuseMemory(pg_rec->par_formats);
		
		first_run = UseMemory(pg_rec->t_style_run);
		first_run[1].offset = ZERO_TEXT_PAD;
		UnuseMemory(pg_rec->t_style_run);
	
		first_run = UseMemory(pg_rec->par_style_run);
		first_run[1].offset = ZERO_TEXT_PAD;
		UnuseMemory(pg_rec->par_style_run);

		pgResetDocBounds(pg_rec);
	
		SetMemoryPurge(pg_rec->vis_area, NO_PURGING_STATUS, FALSE);
		SetMemoryPurge(pg_rec->select, NO_PURGING_STATUS, FALSE);
		SetMemoryPurge(pg_rec->hilite_rgn, NO_PURGING_STATUS, FALSE);
		SetMemoryPurge(pg_rec->t_blocks, NO_PURGING_STATUS, FALSE);
		SetMemoryPurge(pg, NO_PURGING_STATUS, FALSE);
		SetMemoryPurge(pg_rec->t_style_run, 0x00A0, FALSE);
		SetMemoryPurge(pg_rec->par_style_run, 0x00A0, FALSE);

		++globals->mem_globals->next_mem_id;
		globals->mem_globals->current_id = globals->mem_globals->next_mem_id;

		pg_rec->doc_info.attributes = NO_CLIP_PAGE_AREA;

#ifdef PG_OITC
		list_rec.pg = pg;
		list_rec.flags = 0;
		pgAppendToList(globals->pg_list, &list_rec);
#endif
	}
	
	PG_CATCH {
		
		pgFailureDispose(pg);
		return	MEM_NULL;
	}
	
	PG_ENDTRY;

	UnuseMemory(pg);
	
	globals->pg_extend((void PG_FAR *) pg, pg_new);

	return pg;
}


/* pgNewShared creates a new pg_ref but uses shared_from common structures. The
remaining parameters are shared if they corresponding item is NULL. */

PG_PASCAL (pg_ref) pgNewShared (pg_ref shared_from, const generic_var def_device,
		shape_ref vis_area, shape_ref page_area, shape_ref exclude_area,
		long attributes)
{
	paige_rec_ptr	shared_rec;
	pg_ref			new_ref;
	generic_var		use_device;
	long			shared_flags;

	if (!def_device)
		use_device = USE_NO_DEVICE;
	
	shared_rec = UseMemory(shared_from);
	new_ref = pgNew(shared_rec->globals, use_device, vis_area, page_area, exclude_area, attributes);
	shared_flags = 0;
	
	if (!def_device)
		shared_flags |= SHARED_GRAF_DEVICE;
	if (!vis_area)
		shared_flags |= SHARED_VIS_AREA;
	if (!page_area)
		shared_flags |= SHARED_PAGE_AREA;
	if (!exclude_area)
		shared_flags |= SHARED_EXCLUDE_AREA;

	UnuseMemory(shared_from);
	
	pgShareRefs(new_ref, shared_from, shared_flags);
	
	return	new_ref;
}


/* pgShareRefs causes pg to share common structures from shared_from. The purpose of this is
to create one common set of formats and (optionally) the page and vis shapes. */

PG_PASCAL (void) pgShareRefs (pg_ref pg, pg_ref shared_from, long shared_flags)
{
	paige_rec_ptr	pg_rec, shared_rec;
	long			previous_shared_flags;

	shared_rec = UseMemory(shared_from);
	pg_rec = UseMemory(pg);
	previous_shared_flags = pg_rec->shared_flags;
	pg_rec->shared_pg = shared_from;
	pg_rec->shared_flags = shared_flags | SHARED_FORMATS;

	DisposeMemory(pg_rec->t_formats);
	DisposeMemory(pg_rec->par_formats);
	DisposeMemory(pg_rec->fonts);
	DisposeMemory(pg_rec->named_styles);
	
	pg_rec->t_formats = shared_rec->t_formats;
	pg_rec->par_formats = shared_rec->par_formats;
	pg_rec->fonts = shared_rec->fonts;
	pg_rec->named_styles = shared_rec->named_styles;

	if (shared_flags & SHARED_GRAF_DEVICE) {

		if (!(pg_rec->flags & NO_DEVICE_BIT) && !(previous_shared_flags & SHARED_GRAF_DEVICE))
			pgCloseDevice(pg_rec->globals, &pg_rec->port);

		pgBlockMove(&shared_rec->port, &pg_rec, sizeof(graf_device));
		pg_rec->flags &= (~NO_DEVICE_BIT);
		pg_rec->flags |= (shared_rec->flags & NO_DEVICE_BIT);
	}

	if (shared_flags & SHARED_VIS_AREA) {
		
		pgDisposeShape(pg_rec->vis_area);
		pg_rec->vis_area = shared_rec->vis_area;
	}

	if (shared_flags & SHARED_PAGE_AREA) {
		
		pgDisposeShape(pg_rec->wrap_area);
		pg_rec->wrap_area = shared_rec->wrap_area;
	}

	if (shared_flags & SHARED_EXCLUDE_AREA) {
		
		if (pg_rec->exclude_area)
			pgDisposeShape(pg_rec->exclude_area);

		pg_rec->exclude_area = shared_rec->exclude_area;
	}

	UnuseMemory(shared_from);
	UnuseMemory(pg);
}


/* pgDispose -- Disposes a pg_ref (deallocates everything in it).  */

PG_PASCAL (void) pgDispose (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	pg_globals_ptr		globals;
	pgm_globals_ptr		mem_globals;
	memory_ref			old_sublist;
	long				memory_id, cache_index;
	short				i;
	
	globals = pgGetGlobals(pg);
	globals->pg_extend((void PG_FAR *) pg, pg_dispose);
	mem_globals = globals->mem_globals;

	pg_rec = UseMemory(pg);
	
	if ((cache_index = pgInCacheList(mem_globals, pg) > 0)) {
		
		DeleteMemory(mem_globals->freemem_info, cache_index - 1, 1);
		pg_rec->cache_file = MEM_NULL;
	}

	if (old_sublist = pgGetSubrefState(pg_rec, TRUE))
		DisposeMemory(old_sublist);
		
	//Dispose of headers and footers
	for (i = 0; i < 4; i++) {
		pgFailureDispose(pg_rec->headers[i]);
		pgFailureDispose(pg_rec->footers[i]);
	}
	
	pgCallTextHook(pg_rec, NULL, pgdispose_reason, 0, pg_rec->t_length, call_for_delete, 0, 0, 0);
	
	if (!(pg_rec->shared_flags & SHARED_FORMATS))
		pgWillDeleteFormats(pg_rec, pg_rec->globals, pgdispose_reason, pg_rec->t_formats, pg_rec->par_formats);

	if (pg_rec->merge_save)
		pgDispose(pg_rec->merge_save);
	if (pg_rec->applied_range)
		DisposeMemory(pg_rec->applied_range);
	if (pg_rec->list_columns)
		DisposeMemory(pg_rec->list_columns);

#ifdef PG_SUBSETS
	pgDisposeAllSubRefs(pg_rec->t_blocks);
#endif

	if (!(pg_rec->flags & NO_DEVICE_BIT) && !(pg_rec->shared_flags & SHARED_GRAF_DEVICE))
		pgCloseDevice(pg_rec->globals, &pg_rec->port);

	memory_id = pg_rec->mem_id;
	UnuseMemory(pg);

	DisposeAllMemory(mem_globals, memory_id);

#ifdef PG_OITC
	if ((memory_id = pgFindInList(globals->pg_list, NULL, pgListCompareLong, &pg)) != NOT_IN_LIST)
		pgRemoveFromList(globals->pg_list, memory_id, NULL);
#endif
}


/* pgFailureDispose is identical to pgDispose except it is called when an
exception handler causes a function to abort that might have created only a
partial pg_ref and/or in debug mode, allocations in a "used" state.  */

PG_PASCAL (void) pgFailureDispose (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	pg_globals_ptr		globals;
	pgm_globals_ptr		mem_globals;
	long				memory_id;
	short				i;

	if (!pg)
		return;

	if (globals = pgGetGlobals(pg))
		globals->pg_extend((void PG_FAR*) pg, pg_dispose);

	pg_rec = UseMemory(pg);

	//Dispose of headers and footers
	for (i = 0; i < 4; i++) {
		pgFailureDispose(pg_rec->headers[i]);
		pgFailureDispose(pg_rec->footers[i]);
	}
	
	if (pg_rec->t_blocks)
		pgCallTextHook(pg_rec, NULL, pgdispose_reason, 0, pg_rec->t_length, call_for_delete, 0, 0, 0);
	
	if (pg_rec->t_formats && pg_rec->t_style_run && pg_rec->par_formats
			&& pg_rec->par_style_run)
		pgWillDeleteFormats(pg_rec, pg_rec->globals, pgdispose_reason, pg_rec->t_formats, pg_rec->par_formats);
	
	if (pg_rec->merge_save)
		pgFailureDispose(pg_rec->merge_save);
	
	if (pg_rec->applied_range)
		DisposeFailedMemory(pg_rec->applied_range);

	if (!(pg_rec->flags & NO_DEVICE_BIT))
		pgCloseDevice(pg_rec->globals, &pg_rec->port);

	memory_id = pg_rec->mem_id;
	mem_globals = pg_rec->globals->mem_globals;
	UnuseMemory(pg);
	
	DisposeAllFailedMemory(mem_globals, memory_id);

#ifdef PG_OITC
	if ((memory_id = pgFindInList(globals->pg_list, NULL, pgListCompareLong, &pg)) != NOT_IN_LIST)
		pgRemoveFromList(globals->pg_list, memory_id, NULL);
#endif
}


/* pgNewShell creates a new pgRef but with no shapes, using only defaults for
everything else.  The usual purpose is to create the minimum pgRef to perform
a file "open" or some similar feature.  */

PG_PASCAL (pg_ref) pgNewShell (const pg_globals_ptr globals)
{
	return	pgNew(globals, (generic_var)USE_NO_DEVICE, (shape_ref)MEM_NULL, (shape_ref)MEM_NULL, (shape_ref)MEM_NULL, 0);
}


/* pgDuplicate returns a new empty pgRef based on the attributes in pg. The
style and font are set to the defaults.  */

PG_PASCAL (pg_ref) pgDuplicate (pg_ref pg)
{
	paige_rec_ptr					pg_rec, target_rec;
	pg_ref							result;
	
	pg_rec = UseMemory(pg);

	PG_TRY(pg_rec->globals->mem_globals) {

		result = pgNew(pg_rec->globals, (generic_var)pg_rec->port.machine_var, pg_rec->vis_area, pg_rec->wrap_area, pg_rec->exclude_area,
				pg_rec->flags);
	}
	PG_CATCH
	{
		UnuseMemory(pg);
		PG_RERAISE();
	}
	PG_ENDTRY

	target_rec = UseMemory(result);
	target_rec->flags |= NO_DEVICE_BIT;
	target_rec->doc_info = pg_rec->doc_info;
	target_rec->flags2 = pg_rec->flags2;

	UnuseMemory(pg);
	UnuseMemory(result);

	return	result;
}


/* pgSetDefaultStyle sets the default style of pg to def_style. */

PG_PASCAL (void) pgSetDefaultStyle (pg_ref pg, const style_info_ptr def_style, short draw_mode)
{
	paige_rec_ptr			pg_rec;
	style_info_ptr			style;
	long					real_used_ctr;

	pg_rec = UseMemory(pg);
	style = UseMemoryRecord(pg_rec->t_formats, pg_rec->def_style_index, 0, TRUE);
	real_used_ctr = style->used_ctr;
	pgBlockMove(def_style, style, sizeof(style_info));
	style->used_ctr = real_used_ctr;
	UnuseMemory(pg_rec->t_formats);
	pgInvalSelect(pg, 0, pg_rec->t_length);

	if (draw_mode)
		pgUpdateText(pg_rec, NULL, 0, pg_rec->t_length, MEM_NULL, NULL, draw_mode, TRUE);

	UnuseMemory(pg);
}


/* pgGetDefaultStyle returns the default style for pg. */

PG_PASCAL (pg_short_t) pgGetDefaultStyle (pg_ref pg, style_info_ptr def_style)
{
	paige_rec_ptr			pg_rec;
	pg_short_t				index;
	
	pg_rec = UseMemory(pg);
	index = pg_rec->def_style_index;
	GetMemoryRecord(pg_rec->t_formats, index, def_style);
	UnuseMemory(pg);
	
	return	index;
}


/* pgSetDefaultPar sets the default para style of pg to def_par. */

PG_PASCAL (void) pgSetDefaultPar (pg_ref pg, const par_info_ptr def_par, short draw_mode)
{
	paige_rec_ptr			pg_rec;
	par_info_ptr			par;
	long					real_used_ctr;

	pg_rec = UseMemory(pg);
	par = UseMemory(pg_rec->par_formats);
	real_used_ctr = par->used_ctr;
	pgBlockMove(def_par, par, sizeof(par_info));
	par->used_ctr = real_used_ctr;
	UnuseMemory(pg_rec->par_formats);
	pgInvalSelect(pg, 0, pg_rec->t_length);

	if (draw_mode)
		pgUpdateText(pg_rec, NULL, 0, pg_rec->t_length, MEM_NULL, NULL, draw_mode, TRUE);

	UnuseMemory(pg);
}

/* pgSetDefaultFont sets the default font of pg to def_font. */

PG_PASCAL (void) pgSetDefaultFont (pg_ref pg, const font_info_ptr def_font, short draw_mode)
{
	paige_rec_ptr			pg_rec;
	font_info_ptr			font;

	pg_rec = UseMemory(pg);
	font = UseMemory(pg_rec->fonts);
	pgBlockMove(def_font, font, sizeof(font_info));
	UnuseMemory(pg_rec->fonts);
	pgInvalSelect(pg, 0, pg_rec->t_length);

	if (draw_mode)
		pgUpdateText(pg_rec, NULL, 0, pg_rec->t_length, MEM_NULL, NULL, draw_mode, TRUE);

	UnuseMemory(pg);
}


/* pgTextSize returns the total bytesize of text */

PG_PASCAL (long) pgTextSize (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	long				t_size;

	pg_rec = UseMemory(pg);
	t_size = pg_rec->t_length;
	UnuseMemory(pg);
	
	return	t_size;
}


/* pgGetAttributes returns the current flag settings in a paige object */

PG_PASCAL (long) pgGetAttributes (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	long				result;
	
	pg_rec = UseMemory(pg);
	result = pg_rec->flags;
	UnuseMemory(pg);
	
	return	result;
}


/* pgGetAttributes2 returns the current flags2 settings in a paige object */

PG_PASCAL (long) pgGetAttributes2 (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	long				result;
	
	pg_rec = UseMemory(pg);
	result = pg_rec->flags2;
	UnuseMemory(pg);
	
	return	result;
}



/* pgSetAttributes changes the attribute settings in a paige object. If
necessary the text is invalidated.  */

PG_PASCAL (pg_boolean) pgSetAttributes (pg_ref pg, long attributes)
{
	paige_rec_ptr		pg_rec;
	long				old_attributes, hide_text_change;
	pg_boolean			result;

	pg_rec = UseMemory(pg);
	old_attributes = pg_rec->flags;
	pg_rec->flags = attributes;
	
	if (attributes & LINE_EDITOR_BIT)
		pg_rec->flags |= (NO_WRAP_BIT | NO_LF_BIT);
	
	if (attributes & EXTERNAL_SCROLL_BIT)
		pg_rec->scroll_pos.h = pg_rec->scroll_pos.v = 0;

	hide_text_change = ((old_attributes & NO_HIDDEN_TEXT_BIT) != (pg_rec->flags & NO_HIDDEN_TEXT_BIT));

	if (((old_attributes & NO_WRAP_BIT) != (pg_rec->flags & NO_WRAP_BIT))
		|| hide_text_change) {
	
		pgInvalSelect(pg, 0, pg_rec->t_length);
		++pg_rec->change_ctr;
		
		if (hide_text_change)
			pgInvalCharLocs(pg_rec);

		if (hide_text_change && (pg_rec->flags & NO_HIDDEN_TEXT_BIT)) {
			pg_short_t			select_ctr;
			t_select_ptr		selections;
			style_info_ptr		select_style;
			pg_boolean			select_changed;
			long				offset_for_style;
			
			if (!(select_ctr = pg_rec->num_selects * 2))
				++select_ctr;

			selections = UseMemory(pg_rec->select);
			select_changed = FALSE;

			while (select_ctr) {
				
				offset_for_style = selections->offset;
				
				if (offset_for_style && (!pg_rec->num_selects))
					--offset_for_style;
				
				select_style = pgFindTextStyle(pg_rec, offset_for_style);
				
				if (select_style->styles[hidden_text_var])
					select_changed |= pgAlignSelection(pg_rec, &selections->offset);

				UnuseMemory(pg_rec->t_formats);
				
				++selections;
				--select_ctr;
			}

			UnuseMemory(pg_rec->select);
			
			if (pg_rec->num_selects) {
				pg_short_t			rec_to_delete, delete_qty;
				
				while (delete_qty = pgFindEmptyHilite(pg_rec, &rec_to_delete))
					DeleteMemory(pg_rec->select, rec_to_delete, delete_qty);
			}

			if (!pg_rec->num_selects)
				pgSetNextInsertIndex(pg_rec);
		}
	}

	result = (old_attributes != pg_rec->flags);

	UnuseMemory(pg);
	
	return	result;
}


/* pgSetAttributes2 changes the attribute settings, flags2, in a paige object. */

PG_PASCAL (pg_boolean) pgSetAttributes2 (pg_ref pg, long attributes2)
{
	paige_rec_ptr		pg_rec;
	long				old_attributes;

	pg_rec = UseMemory(pg);
	old_attributes = pg_rec->flags2;
	pg_rec->flags2 = attributes2;
	UnuseMemory(pg);
	
	return	(old_attributes != pg_rec->flags2);
}


/* pgGetDocInfo returns the current doc_info from pg */

PG_PASCAL (void) pgGetDocInfo (pg_ref pg, pg_doc_ptr doc_info)
{
	paige_rec_ptr		pg_rec;
	
	pg_rec = UseMemory(pg);
	pgBlockMove(&pg_rec->doc_info, doc_info, sizeof(pg_doc_info));
	UnuseMemory(pg);
}


/* pgSetDocInfo changes the document info in pg, re-drawing/re-calcing if
necessary. */

PG_PASCAL (void) pgSetDocInfo (pg_ref pg, const pg_doc_ptr doc_info,
		pg_boolean inval_text, short draw_mode)
{
	paige_rec_ptr		pg_rec;
	pg_boolean			page_info_changed;

	pg_rec = UseMemory(pg);
	
	page_info_changed = ( (doc_info->attributes != pg_rec->doc_info.attributes)
		|| (doc_info->repeat_offset.h != pg_rec->doc_info.repeat_offset.h)
		|| (doc_info->repeat_offset.v != pg_rec->doc_info.repeat_offset.v)
		|| (doc_info->minimum_orphan != pg_rec->doc_info.minimum_orphan)
		|| (doc_info->minimum_widow != pg_rec->doc_info.minimum_widow));
	
	if (page_info_changed)
		pg_rec->port.clip_info.change_flags |= CLIP_PAGE_CHANGED;

	if (inval_text) {

		pgInvalSelect(pg, 0, pg_rec->t_length);

		if (page_info_changed)
			pg_rec->flags |= DOC_BOUNDS_DIRTY;
	}

	pgBlockMove(doc_info, &pg_rec->doc_info, sizeof(pg_doc_info));

	if (!(pg_rec->doc_info.attributes & BOTTOM_FIXED_BIT))
		pg_rec->doc_info.attributes |= NO_CLIP_PAGE_AREA;

	if (draw_mode)  {
		short	use_draw_mode;

		if ((use_draw_mode = draw_mode) == best_way)
				use_draw_mode = bits_copy;

		pgUpdateText(pg_rec, NULL, 0, pg_rec->t_length, MEM_NULL, NULL,
			use_draw_mode, TRUE);
	}

	UnuseMemory(pg);
}


/* pgGetChangeCtr returns the change counter in pg */

PG_PASCAL (long) pgGetChangeCtr (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	long				result;

	pg_rec = UseMemory(pg);
	result = pg_rec->change_ctr;
	UnuseMemory(pg);
	
	return	result;
}


/* pgSetChangeCtr sets the change counter in pg */

PG_PASCAL (void) pgSetChangeCtr (pg_ref pg, long ctr)
{
	paige_rec_ptr		pg_rec;

	pg_rec = UseMemory(pg);
	pg_rec->change_ctr = ctr;
	UnuseMemory(pg);
}


/* pgSetDefaultDevice sets the default graphics device */

PG_PASCAL (void) pgSetDefaultDevice (pg_ref pg, const graf_device_ptr device)
{
	paige_rec_ptr		pg_rec;
	
	pg_rec = UseMemory(pg);
	
	if (!(pg_rec->flags & NO_DEVICE_BIT))
		pgCloseDevice(pg_rec->globals, &pg_rec->port);
	
	pgBlockMove(device, &pg_rec->port, sizeof(graf_device));
	pg_rec->flags &= (~NO_DEVICE_BIT);
	pg_rec->scale_factor.scale = pg_rec->real_scaling;
	pgScaleGrafDevice(pg_rec);
	pg_rec->port.clip_info.change_flags |= CLIP_DEVICE_CHANGED;

	UnuseMemory(pg);
}

/* pgGetDefaultDevice returns the default graphics device.  */

PG_PASCAL (void) pgGetDefaultDevice (pg_ref pg, graf_device_ptr device)
{
	paige_rec_ptr		pg_rec;
	
	pg_rec = UseMemory(pg);
	pgBlockMove(&pg_rec->port, device, sizeof(graf_device));
	UnuseMemory(pg);
}


/* pgInitSameDevice is identical to pgInitDevice except the same drawing port
is used from pg.  (This in effect "copies" the port in pg).  */

PG_PASCAL (void) pgInitSameDevice (pg_ref pg, const graf_device_ptr device)
{
	paige_rec_ptr		pg_rec;
	
	pg_rec = UseMemory(pg);
	pgInitDevice(pg_rec->globals, pg_rec->port.machine_var, pg_rec->port.machine_ref, device);

	UnuseMemory(pg);
}


/* pgSetDevicePalette sets the machine-specific device palette for the pg_ref) */

PG_PASCAL (void) pgSetDevicePalette (pg_ref pg, const generic_var palette)
{
	paige_rec_ptr		pg_rec;
	
	pg_rec = UseMemory(pg);
	pg_rec->port.palette = palette;
	UnuseMemory(pg);
}

/* pgGetDevicePalette returns the palette device used by pg */

PG_PASCAL (generic_var) pgGetDevicePalette (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	generic_var			palette;
	
	pg_rec = UseMemory(pg);
	palette = pg_rec->port.palette;
	UnuseMemory(pg);
	
	return	palette;
}


/* pgSetHooks replaces function pointers in pg with *hooks.  If inval_text is
TRUE the text is invalidated (so it re-calcs).  */

PG_PASCAL (void) pgSetHooks (pg_ref pg, const pg_hooks PG_FAR *hooks, pg_boolean inval_text)
{
	paige_rec_ptr		pg_rec;
	
	pg_rec = UseMemory(pg);
	pgBlockMove(hooks, &pg_rec->procs, sizeof(pg_hooks));
	
	if (inval_text)
		pgInvalSelect(pg, 0, pg_rec->t_length);

	UnuseMemory(pg);
}


/* pgGetHooks returns the current function pointers in pg.  This is essential
before changing some of them and calling pgSetHooks.  */

PG_PASCAL (void) pgGetHooks (pg_ref pg, pg_hooks PG_FAR *hooks)
{
	paige_rec_ptr		pg_rec;
	
	pg_rec = UseMemory(pg);
	pgBlockMove(&pg_rec->procs, hooks, sizeof(pg_hooks));

	UnuseMemory(pg);
}


/* pgSetOrigin changes pg's drawing origin (for all text and shapes). */

PG_PASCAL (void) pgSetOrigin (pg_ref pg, const co_ordinate_ptr origin)
{
	paige_rec_ptr		pg_rec;
	co_ordinate			old_origin;

	pg_rec = UseMemory(pg);
	old_origin = pg_rec->port.origin;
	pg_rec->port.origin = *origin;
	
	UnuseMemory(pg);
}


/* pgGetOrigin returns pg's drawing origin (for all text and shapes). */

PG_PASCAL (void) pgGetOrigin (pg_ref pg, co_ordinate_ptr origin)
{
	paige_rec_ptr		pg_rec;
	
	pg_rec = UseMemory(pg);
	*origin = pg_rec->port.origin;
	UnuseMemory(pg);
}


/* pgGetGlobals returns the globals pointer taken from a pg_ref. Typical use is
for situations where the app wants the globals but doesn't have access to it,
only a pg_ref is around.  */

PG_PASCAL (pg_globals_ptr) pgGetGlobals (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	pg_globals_ptr		result;
	
	pg_rec = UseMemory(pg);
	result = pg_rec->globals;
	UnuseMemory(pg);
	
	return	result;
}


/* pgSetArea sets one or more areas by copying the contents of the non-NULL
shapes.  */

PG_PASCAL (void) pgSetAreas (pg_ref pg, shape_ref vis_area, shape_ref page_area,
		shape_ref exclude_area)
{
	paige_rec_ptr			pg_rec;
	
	pg_rec = UseMemory(pg);
	
	if (vis_area) {
		rectangle_ptr		vis_ptr;

		MemoryCopy(vis_area, pg_rec->vis_area);
		vis_ptr = UseMemory(vis_area);
		pg_rec->base_vis_origin = vis_ptr->top_left;
		UnuseMemory(vis_area);
		
		pg_rec->port.clip_info.change_flags |= CLIP_VIS_CHANGED;
	}

	if (page_area) {
		pg_rec->port.clip_info.change_flags |= CLIP_PAGE_CHANGED;
		MemoryCopy(page_area, pg_rec->wrap_area);
	}
	if (exclude_area) {

		pg_rec->port.clip_info.change_flags |= CLIP_EXCLUDE_CHANGED;
		MemoryCopy(exclude_area, pg_rec->exclude_area);
	}
	
	if (page_area || exclude_area) {
		
		pgResetDocBounds(pg_rec);

		pgInvalSelect(pg, 0, pg_rec->t_length);
		++pg_rec->change_ctr;
	}
	
	UnuseMemory(pg);
}


/* pgGetAreas fills in each non-NULL shape with the respective areas.  */

PG_PASCAL (void) pgGetAreas (pg_ref pg, shape_ref vis_area, shape_ref page_area,
		shape_ref exclude_area)
{
	paige_rec_ptr			pg_rec;
	
	pg_rec = UseMemory(pg);
	
	if (vis_area)
		MemoryCopy(pg_rec->vis_area, vis_area);
	if (page_area)
		MemoryCopy(pg_rec->wrap_area, page_area);
	if (exclude_area)
		MemoryCopy(pg_rec->exclude_area, exclude_area);

	UnuseMemory(pg);
}


/* pgGetPageArea returns the actual wrap area ref */

PG_PASCAL (shape_ref) pgGetPageArea (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	shape_ref			result;
	
	pg_rec = UseMemory(pg);
	result = pg_rec->wrap_area;
	UnuseMemory(pg);
	
	return	result;
}

/* pgGetVisArea returns the actual vis area ref */

PG_PASCAL (shape_ref) pgGetVisArea (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	shape_ref			result;
	
	pg_rec = UseMemory(pg);
	result = pg_rec->vis_area;
	UnuseMemory(pg);
	
	return	result;
}


/* pgGetExcludeArea returns the actual exclude area ref */

PG_PASCAL (shape_ref) pgGetExcludeArea (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	shape_ref			result;
	
	pg_rec = UseMemory(pg);
	result = pg_rec->exclude_area;
	UnuseMemory(pg);

	return	result;
}



/* pgGrowVisArea enlarges the vis area by top_left amounts and/or
bot_right amounts. Either parameter can be null.  */

PG_PASCAL (void) pgGrowVisArea (pg_ref pg, const co_ordinate_ptr top_left, const co_ordinate_ptr bot_right)
{
	paige_rec_ptr			pg_rec;
	register rectangle_ptr	vis_ptr;
	register pg_short_t		num_rects;
	
	pg_rec = UseMemory(pg);
	
	for (vis_ptr = UseMemory(pg_rec->vis_area), num_rects = (pg_short_t)GetMemorySize(pg_rec->vis_area);
			num_rects;  ++vis_ptr, --num_rects) {
		
		if (top_left)
			pgAddPt(top_left, &vis_ptr->top_left);
		
		if (bot_right)
			pgAddPt(bot_right, &vis_ptr->bot_right);
	}
	
	UnuseMemory(pg_rec->vis_area);

	pg_rec->port.clip_info.change_flags |= CLIP_VIS_CHANGED;

	UnuseMemory(pg);
}


/* pgOffsetAreas moves some or all of the pg shapes by h and v amounts. Each
shape moves if the parameter is TRUE. */

PG_PASCAL (void) pgOffsetAreas (pg_ref pg, long h, long v, pg_boolean offset_page,
		pg_boolean offset_vis, pg_boolean offset_exclude)
{
	paige_rec_ptr				pg_rec;
	register text_block_ptr		block;
	register point_start_ptr	starts;
	register long				num_blocks, num_lines;

	pg_rec = UseMemory(pg);
	
	if (h || v) {
	
		if (offset_page) {
			
			pg_rec->port.clip_info.change_flags |= CLIP_PAGE_CHANGED;
			pgOffsetShape(pg_rec->wrap_area, h, v);
		}
		if (offset_vis) {
			
			pg_rec->port.clip_info.change_flags |= CLIP_VIS_CHANGED;
			pgOffsetShape(pg_rec->vis_area, h, v);
		}
		if (offset_exclude) {
			
			pg_rec->port.clip_info.change_flags |= CLIP_EXCLUDE_CHANGED;
			pgOffsetShape(pg_rec->exclude_area, h, v);
		}
		
		pgResetDocBounds(pg_rec);
	
		if ((offset_exclude || pgEmptyShape(pg_rec->exclude_area)) && offset_page) {
			
			block = UseMemory(pg_rec->t_blocks);
			
			for (num_blocks = GetMemorySize(pg_rec->t_blocks); num_blocks;
					++block, --num_blocks) {
				
				pgOffsetRect(&block->bounds, h, v);
				
				if (block->flags & SOME_LINES_GOOD && !(block->flags & LINES_PURGED)) {
					
					starts = UseMemory(block->lines);
					
					for (num_lines = GetMemorySize(block->lines); num_lines;
							++starts, --num_lines)
						pgOffsetRect(&starts->bounds, h, v);
					
					UnuseMemory(block->lines);
				}
				else
				if (!(block->flags & LINES_PURGED))
					block->flags |= NEEDS_CALC;

				pgOffsetRect(&block->end_start.bounds, h, v);
			}
			
			UnuseMemory(pg_rec->t_blocks);
		}
		else
			pgInvalSelect(pg, 0, pg_rec->t_length);
	}

	UnuseMemory(pg);
}



/* pgErasePageArea fills the page_area with the current background color
(defined in pg) if non-white, or pg port's background if white.
The appropriate port is set and clipped if vis_area is NULL, or the clip is
set to vis_area if non-NULL.  */

PG_PASCAL (void) pgErasePageArea (pg_ref pg, shape_ref vis_area)
{
	paige_rec_ptr		pg_rec;

	pg_rec = UseMemory(pg);
	pgDrawPages(pg_rec, NULL, NULL, vis_area, sp_erase_all_pages);
	UnuseMemory(pg);
}


/* pgSetExtraStruct stores an arbitrary long or pointer into a paige object.
The ref_id is an index into the extra struct buffer. If necessary, the extra
stuff buffer will get extended if the ID is out of range.   */

PG_PASCAL (void) pgSetExtraStruct (pg_ref pg, const void PG_FAR *extra_struct,
		long ref_id)
{
	paige_rec_ptr		pg_rec;
	long PG_FAR			*extra_ptr;
	long				current_size;
	long				id;

	if ((id = ref_id + EXTRA_STRUCT_RSRV) < 0)
		return;

	pg_rec = UseMemory(pg);

	pgInitExtraStruct(pg_rec);

	current_size = GetMemorySize(pg_rec->extra_stuff);
	
	if (id >= current_size)
		SetMemorySize(pg_rec->extra_stuff, id + 1);

	extra_ptr = UseMemoryRecord(pg_rec->extra_stuff, id, 0, TRUE);
	*extra_ptr = (long) extra_struct;
	
	UnuseMemory(pg_rec->extra_stuff);
	UnuseMemory(pg);
}


PG_PASCAL (void PG_FAR *) pgGetExtraStruct (pg_ref pg, long ref_id)
{
	paige_rec_ptr		pg_rec;
	void PG_FAR			*result;
	long PG_FAR			*extra_ptr;
	long				current_size;
	long				id;

	pg_rec = UseMemory(pg);
	result = NULL;

	if (((id = ref_id + EXTRA_STRUCT_RSRV) >= 0) && pg_rec->extra_stuff) {
	
		current_size = GetMemorySize(pg_rec->extra_stuff);
		
		if (id < current_size) {
			
			extra_ptr = UseMemory(pg_rec->extra_stuff);
			result = (void PG_FAR *) extra_ptr[id];
			UnuseMemory(pg_rec->extra_stuff);
		}
	}

	UnuseMemory(pg);
	
	return	result;
}


/* pgInitExtraStruct is a low-level utility that initializes extra_struct. */

PG_PASCAL (void) pgInitExtraStruct (paige_rec_ptr pg)
{
	if (!pg->extra_stuff) {
		
		pgPushMemoryID(pg);

		pg->extra_stuff = MemoryAllocClear(pg->globals->mem_globals,
				sizeof(long), EXTRA_STRUCT_RSRV, 4);
		
		pgPopMemoryID(pg);
	}
}


/* pgExtraUniqueID returns a "unique" ID, which is really the first NULL entry
it finds in the extra_struct list, or if none found the appended record. */

PG_PASCAL (long) pgExtraUniqueID (pg_ref pg)
{
	paige_rec_ptr		pg_rec;
	long PG_FAR			*extra_ptr;
	long				extra_size;
	long				next_id;
	
	pg_rec = UseMemory(pg);
	next_id = EXTRA_STRUCT_RSRV;

	if (pg_rec->extra_stuff) {
		
		extra_ptr = UseMemory(pg_rec->extra_stuff);
		extra_ptr += EXTRA_STRUCT_RSRV;
		extra_size = GetMemorySize(pg_rec->extra_stuff) - EXTRA_STRUCT_RSRV;

		while (extra_size) {
			
			if (!(*extra_ptr))
				break;
			
			++extra_ptr;
			++next_id;
			--extra_size;
		}
		
		UnuseMemory(pg_rec->extra_stuff);
	}

	UnuseMemory(pg);
	
	return	next_id;
}


/* pgSetScaling sets the scale_factor, re-drawing if necessary */

PG_PASCAL (void) pgSetScaling (pg_ref pg, const pg_scale_ptr scale_factor, short draw_mode)
{
	paige_rec_ptr			pg_rec;

	pg_rec = UseMemory(pg);
	pgInvalidateHilite(pg_rec, (pg_boolean)(draw_mode != draw_none));
	pg_rec->port.clip_info.change_flags |= CLIP_DEVICE_CHANGED;
	pg_rec->scale_factor = *scale_factor;
	pg_rec->real_scaling = scale_factor->scale;
	
	if (pg_rec->scale_factor.scale) {
		long		scale_low, scale_hi;
		
		scale_low = pg_rec->scale_factor.scale & 0xFFFF;
		scale_hi = pg_rec->scale_factor.scale >> 16;
		
		if (scale_low == scale_hi)
			pg_rec->scale_factor.scale = 0;
	}
    
	pgScaleGrafDevice(pg_rec);
	
	if (draw_mode)
		pgUpdateText(pg_rec, NULL, 0, pg_rec->t_length, MEM_NULL, NULL,
			draw_mode, TRUE);

	UnuseMemory(pg);
}


/* pgGetScaling returns the current scale_factor */

PG_PASCAL (void) pgGetScaling (pg_ref pg, pg_scale_ptr scale_factor)
{
	paige_rec_ptr			pg_rec;

	pg_rec = UseMemory(pg);
	*scale_factor = pg_rec->scale_factor;
	scale_factor->scale = pg_rec->real_scaling;
	UnuseMemory(pg);
}


/* Returns document height   */

PG_PASCAL (long) pgTotalTextHeight (pg_ref pg, short paginate)
{
	paige_rec_ptr			pg_rec;
	memory_ref				subref_state;
	long					height;
	
	pg_rec = UseMemory(pg);
	subref_state = pgGetSubrefState(pg_rec, TRUE);

	if (paginate)
		pgComputeDocHeight(pg_rec, TRUE);

	height = pg_rec->doc_bounds.bot_right.v - pg_rec->doc_bounds.top_left.v;
	
	if (subref_state)
		pgRestoreSubRefs(pg_rec, subref_state);

	UnuseMemory(pg);
	
	return	height;
}


/* pgInCacheList locates pg within the cache list, if any, and returns the index + 1 if found. */

PG_PASCAL (long) pgInCacheList (pgm_globals_ptr mem_globals, pg_ref pg)
{
	pg_ref  PG_FAR			*refs;
	long					num_refs, index, result;
	
	if (!mem_globals->freemem_info)
		return	0;
	
	num_refs = GetMemorySize(mem_globals->freemem_info);
	refs = UseMemory(mem_globals->freemem_info);
	
	for (index = result = 0; index < num_refs; ++index)
		if (refs[index] == pg) {
			
			result = index + 1;
			break;
		}

	UnuseMemory(mem_globals->freemem_info);
	
	return		result;
}


/******************************* Local Functions **************************/


/* This gets called a few times by pgNew: if shape_param is NULL a new, empty
shape is returned, otherwise a copy is returned.  */

static shape_ref copy_shape_param (pg_globals_ptr globals, shape_ref shape_param)
{
	if (shape_param)
		return	MemoryDuplicate(shape_param);
	
	return	pgRectToShape(globals->mem_globals, NULL);
}


/* free_cached_blocks walks through pg and locates which text blocks, if
any, can be unloaded. This is for the 2.0 disk paging (cache) system. The
ref_mask memory_ref cannot be freed, however. If we unload >= needed_free,
we exit.  */

static long free_cached_blocks (paige_rec_ptr pg, memory_ref ref_mask, long needed_free)
{
	rectangle				vis_bounds;
	text_block_ptr			block;
	long					num_blocks;
	long					freed = 0;

	pgShapeBounds(pg->vis_area, &vis_bounds);
	pg->port.scale.scale = -pg->port.scale.scale;
	pgScaleRect(&pg->port.scale, NULL, &vis_bounds);
	pg->port.scale.scale = -pg->port.scale.scale;
	pgOffsetRect(&vis_bounds, pg->scroll_pos.h, pg->scroll_pos.v);
	pgOffsetRect(&vis_bounds, -pg->port.origin.h, -pg->port.origin.v);
	pgInsetRect(&vis_bounds, 32, 32);

	num_blocks = GetMemorySize(pg->t_blocks);
	block = UseMemory(pg->t_blocks);
	
	while (num_blocks) {
		
		if (!(block->cache_flags & CACHE_LOCK_FLAG))
			if (block->text && block->text != ref_mask)
				if (!(block->cache_flags & CACHE_CHANGED_FLAG))
					if (!GetAccessCtr(block->text))
						if (!(GetMemoryPurge(block->text) & PURGED_FLAG)) {
					
						if ( (block->flags & (NEEDS_CALC | NEEDS_PAGINATE))
								|| !pgSectRect(&vis_bounds, &block->bounds, NULL)) {
						
						
							freed += (pgMemorySize(block->text));
	
							DisposeMemory(block->text);
							block->text = MEM_NULL;
						}
		}

		++block;
		--num_blocks;
	}

	UnuseMemory(pg->t_blocks);
	
	return		freed;
}
