/* This file handles "style sheets."  */

#include "Paige.h"

#ifdef MAC_PLATFORM
#pragma segment pgstlsht
#endif

#include "pgEdit.h"
#include "pgDefStl.h"
#include "pgStyles.h"



static short count_style_sheets (paige_rec_ptr pg, short PG_FAR *highest_id);
static short count_par_style_sheets (paige_rec_ptr pg, short PG_FAR *highest_id);
static void affected_text_range (paige_rec_ptr pg, short compare_type,
		short stylesheet_id, select_pair_ptr range);
static pg_boolean compare_stylename (pg_c_string_ptr name1, pg_c_string_ptr name2);


/* pgNewStyle creates a new style sheet record and returns a unique identifier */

PG_PASCAL (short) pgNewStyle (pg_ref pg, const style_info_ptr new_style, const font_info_ptr style_font)
{
	paige_rec_ptr				pg_rec;
	style_info					style_to_add;
	font_info					font_to_add;
	short						new_id;

	pg_rec = UseMemory(pg);
	pgBlockMove(new_style, &style_to_add, sizeof(style_info));
	
	count_style_sheets(pg_rec, &new_id);
	++new_id;
	style_to_add.style_sheet_id = new_id;
	style_to_add.maintenance |= IS_STYLE_SHEET;

	if (style_font) {
		
		pgBlockMove(style_font, &font_to_add, sizeof(font_info));
		style_to_add.font_index = pgAddNewFont(pg_rec, &font_to_add);
	}

	pgAddStyleInfo(pg_rec, NULL, new_stylesheet_reason, &style_to_add);
	++pg_rec->change_ctr;
	
	UnuseMemory(pg);

	return		new_id;
}


/* pgRemoveStyle removes the style sheet style_id.  (Actually, the style won't
get physically removed until it is no longer used by any text).  After this,
the style_id will no longer be recognized.   */

PG_PASCAL (void) pgRemoveStyle (pg_ref pg, short style_id)
{
	paige_rec_ptr			pg_rec;
	style_info_ptr			the_style;

	pg_rec = UseMemory(pg);
	
	if (the_style = pgLocateStyleSheet(pg_rec, style_id, NULL)) {
		
		--the_style->used_ctr;
		the_style->style_sheet_id = 0;
		
		UnuseMemory(pg_rec->t_formats);
	}
	
	++pg_rec->change_ctr;

	UnuseMemory(pg);
}


/* pgNumStyles returns the total number of style sheets available.  */

PG_PASCAL (short) pgNumStyles (pg_ref pg)
{
	paige_rec_ptr				pg_rec;
	short						num_styles;

	pg_rec = UseMemory(pg);
	num_styles = count_style_sheets(pg_rec, NULL);
	UnuseMemory(pg);
	
	return		num_styles;
}


/* pgMaxStylesheetID returns the highest style id used.  */

PG_PASCAL (short) pgMaxStylesheetID (pg_ref pg)
{
	paige_rec_ptr				pg_rec;
	short						num_styles;

	pg_rec = UseMemory(pg);
	count_style_sheets(pg_rec, &num_styles);
	UnuseMemory(pg);
	
	return		num_styles;
}


/* pgGetStyle returns the whole style record belonging to style sheet style_id.
However, if the style is not found the function returns FALSE and style is
not changed.  */

PG_PASCAL (pg_boolean) pgGetStyle (pg_ref pg, short style_id, style_info_ptr style)
{
	paige_rec_ptr			pg_rec;
	style_info_ptr			the_style;
	short					result;

	pg_rec = UseMemory(pg);
	
	if (the_style = pgLocateStyleSheet(pg_rec, style_id, NULL)) {
		
		result = TRUE;
		pgBlockMove(the_style, style, sizeof(style_info));
		UnuseMemory(pg_rec->t_formats);
	}
	else
		result = FALSE;

	UnuseMemory(pg);
	
	return	result;
}

/* pgChangeStyle changes the whole style record for style_id to style and
style_font.  */

PG_PASCAL (void) pgChangeStyle (pg_ref pg, short style_id, const style_info_ptr style,
		const font_info_ptr style_font, short draw_mode)
{
	paige_rec_ptr			pg_rec;
	style_info_ptr			the_style, each_style, caps_style;
	static style_info		old_style, new_caps_style, mask;
	font_info				font_to_add;
	select_pair				range_affected;
	long					qty, change_index, old_caps_index, new_caps_index;
	short					negative_style_id, font_index, use_draw_mode;

	pg_rec = UseMemory(pg);
	
	if (the_style = pgLocateStyleSheet(pg_rec, style_id, NULL)) {
		
		pgBlockMove(the_style, &old_style, SIGNIFICANT_STYLE_SIZE);
		pgBlockMove(style, the_style, SIGNIFICANT_STYLE_SIZE);
		the_style->style_sheet_id = style_id;

		if (style_font) {
			
			pgBlockMove(style_font, &font_to_add, sizeof(font_info));
			the_style->font_index = pgAddNewFont(pg_rec, &font_to_add);
		}
		
		if ((font_index = the_style->font_index) == DEFAULT_FONT_INDEX)
			font_index = 0;

		the_style->procs.init(pg_rec, the_style, UseMemoryRecord(pg_rec->fonts, font_index, 0, TRUE));
		UnuseMemory(pg_rec->fonts);

/* Now walk through all style_info's and locate any "offspring" from this
original stylesheet. If found, I need to change original fields. */

		each_style = UseMemoryRecord(pg_rec->t_formats, 0, USE_ALL_RECS, FALSE);
		negative_style_id = -style_id;
		qty = GetMemorySize(pg_rec->t_formats);
		
		for (change_index = 0; change_index < qty; ++each_style, ++change_index) {
			
			if (each_style->style_sheet_id == negative_style_id && !each_style->embed_object) {
				
				if (each_style->styles[small_caps_var])
					old_caps_index = each_style->small_caps_index + 1;
				else
					old_caps_index = 0;

				pgFillBlock(&mask, SIGNIFICANT_STYLE_SIZE, COMPARE_TRUE);
				pgSetMaskFromFlds(&old_style, each_style, &mask, style_compare, FALSE);
				mask.style_sheet_id = 0;
				pgSetFldsFromMask(each_style, the_style, &mask, SIGNIFICANT_STYLE_SIZE);
				each_style->procs.init(pg_rec, each_style, UseMemoryRecord(pg_rec->fonts, font_index, 0, TRUE));
				UnuseMemory(pg_rec->fonts);
				
				if (each_style->styles[small_caps_var])
					new_caps_index = each_style->small_caps_index + 1;
				else
					new_caps_index = 0;
				
				if ((new_caps_index != old_caps_index) && new_caps_index) {
					
					if (old_caps_index) {
						
						caps_style = UseMemoryRecord(pg_rec->t_formats, old_caps_index - 1, 0, FALSE);
						caps_style->used_ctr -= 1;
					}
					
					new_caps_style = *each_style;
					UnuseMemory(pg_rec->t_formats);
					
					new_caps_index = pgAddSmallCapsStyle(pg_rec, NULL, internal_clone_reason, &new_caps_style);
					each_style = UseMemoryRecord(pg_rec->t_formats, change_index, USE_ALL_RECS, TRUE);
					each_style->small_caps_index = new_caps_index;
					caps_style = UseMemoryRecord(pg_rec->t_formats, new_caps_index, 0, FALSE);
					caps_style->used_ctr += 1;
				}
			}
		}

		UnuseMemory(pg_rec->t_formats);
		
		++pg_rec->change_ctr;

		affected_text_range(pg_rec, style_compare, style_id, &range_affected);
		
		if (range_affected.end > range_affected.begin) {

			pgInvalSelect(pg, range_affected.begin, range_affected.end);

			if (use_draw_mode = draw_mode) {
				
				if (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);
}



/* pgSetStyleSheet changes the text range specieid to the style sheet. */

PG_PASCAL (void) pgSetStyleSheet (pg_ref pg, const select_pair_ptr selection,
		short style_id, short draw_mode)
{
	paige_rec_ptr			pg_rec;
	style_info_ptr			style_sheet;
	style_info				info, mask;

	pg_rec = UseMemory(pg);
	
	if (style_sheet = pgLocateStyleSheet(pg_rec, style_id, NULL)) {

		pgBlockMove(style_sheet, &info, sizeof(style_info));
		UnuseMemory(pg_rec->t_formats);
		pgFillBlock(&mask, sizeof(style_info), -1);
		pg_rec->flags2 |= STYLESHEET_CHANGE;
		pgChangeStyleInfo(pg_rec, selection, &info, &mask, draw_mode);
		pg_rec->flags2 &= (~STYLESHEET_CHANGE);
	}

	UnuseMemory(pg);
}


/* pgGetStyleSheet returns the style sheet ID belonging to the text range
specified.  */

PG_PASCAL (pg_boolean) pgGetStyleSheet (pg_ref pg, const select_pair_ptr selection, short PG_FAR *style_id)
{
	style_info				info, mask;

	pgGetStyleInfo(pg, selection, FALSE, &info, &mask);
	
	if (info.style_sheet_id < 0)
		info.style_sheet_id = -info.style_sheet_id;

	if (style_id)
		*style_id = (short)info.style_sheet_id;
	
	return	(pg_boolean)(mask.style_sheet_id != 0);
}


/* pgFindStyleSheet looks for any stylesheet that matches style. If found the
stylesheet ID is returned (otherwise 0 is returned).  */

PG_PASCAL (short) pgFindStyleSheet (pg_ref pg, const style_info_ptr compare_style,
		const style_info_ptr mask)
{
	paige_rec_ptr				pg_rec;
	style_info					compare_mask;
	register style_info_ptr		target;
	register long				style_qty;
	short						result;

	pg_rec = UseMemory(pg);
	result = 0;
	
	if (mask)
		pgBlockMove(mask, &compare_mask, SIGNIFICANT_STYLE_SIZE);
	else
		pgFillBlock(&compare_mask, SIGNIFICANT_STYLE_SIZE, COMPARE_TRUE);

	compare_mask.style_sheet_id = 0;

	target = UseMemory(pg_rec->t_formats);
	
	for (style_qty = GetMemorySize(pg_rec->t_formats); style_qty; ++target, --style_qty)
		if (target->style_sheet_id > 0) {

			if (pgStyleMatchesCriteria(target, compare_style, &compare_mask,
					NULL, SIGNIFICANT_STYLE_SIZE))
				if ((result = (short)target->style_sheet_id) != 0)
					break;
		}

	UnuseMemory(pg_rec->t_formats);
	UnuseMemory(pg);

	return	result;
}


/* pgGetIndStyleSheet returns the nth stylesheet's ID. If stylesheet ptr is not
NULL, it is filled in with the style. */

PG_PASCAL (short) pgGetIndStyleSheet (pg_ref pg, short index, style_info_ptr stylesheet)
{
	paige_rec_ptr				pg_rec;
	register style_info_ptr		styles;
	register short				style_qty, ctr;
	short						result;

	pg_rec = UseMemory(pg);
	result = 0;

	for (styles = UseMemory(pg_rec->t_formats), style_qty = (pg_short_t)GetMemorySize(pg_rec->t_formats),
			ctr = 0; style_qty; ++styles, --style_qty)
		if (styles->style_sheet_id > 0) {
			
			if (ctr == index) {
				
				result = (short)styles->style_sheet_id;
				if (stylesheet)
					pgBlockMove(styles, stylesheet, sizeof(style_info));
				
				break;
			}

			++ctr;
		}

	UnuseMemory(pg_rec->t_formats);
	UnuseMemory(pg);
	
	return	result;
}



/* pgNewParStyle is the same as pgNewStyle except for paragraphs.  */

PG_PASCAL (short) pgNewParStyle (pg_ref pg, const par_info_ptr new_style)
{
	paige_rec_ptr				pg_rec;
	par_info					style_to_add;
	short						new_id;

	pg_rec = UseMemory(pg);

	pgBlockMove(new_style, &style_to_add, sizeof(par_info));

	count_par_style_sheets(pg_rec, &new_id);
	++new_id;
	style_to_add.style_sheet_id = new_id;
	style_to_add.maintenance |= IS_STYLE_SHEET;

	pgAddParInfo(pg_rec, NULL, new_stylesheet_reason, &style_to_add);
	
	++pg_rec->change_ctr;

	UnuseMemory(pg);

	return		new_id;
}


/* pgRemoveParStyle is the same as pgRemoveStyle except for paragraphs.  */

PG_PASCAL (void) pgRemoveParStyle (pg_ref pg, short style_id)
{
	paige_rec_ptr			pg_rec;
	par_info_ptr			the_style;

	pg_rec = UseMemory(pg);
	
	if (the_style = pgLocateParStyleSheet(pg_rec, style_id)) {
		
		--the_style->used_ctr;
		the_style->style_sheet_id = 0;
		
		UnuseMemory(pg_rec->par_formats);
	}
	
	++pg_rec->change_ctr;
	UnuseMemory(pg);
}


/* pgNumParStyles is the same as pgNumStyles except for paragraphs.  */

PG_PASCAL (short) pgNumParStyles (pg_ref pg)
{
	paige_rec_ptr				pg_rec;
	short						num_styles;

	pg_rec = UseMemory(pg);
	num_styles = count_par_style_sheets(pg_rec, NULL);
	UnuseMemory(pg);
	
	return		num_styles;
}


/* pgMaxParStylesheetID is the same as pgMaxStylesheetID except for paragraphs.  */

PG_PASCAL (short) pgMaxParStylesheetID (pg_ref pg)
{
	paige_rec_ptr				pg_rec;
	short						num_styles;

	pg_rec = UseMemory(pg);
	count_par_style_sheets(pg_rec, &num_styles);
	UnuseMemory(pg);
	
	return		num_styles;
}


/* pgGetParStyle is the same as pgGetStyle except for paragraphs.  */

PG_PASCAL (pg_boolean) pgGetParStyle (pg_ref pg, short style_id, par_info_ptr style)
{
	paige_rec_ptr			pg_rec;
	par_info_ptr			the_style;
	pg_boolean				result;

	pg_rec = UseMemory(pg);
	
	if (the_style = pgLocateParStyleSheet(pg_rec, style_id)) {
		
		result = TRUE;
		pgBlockMove(the_style, style, sizeof(par_info));
		UnuseMemory(pg_rec->par_formats);
	}
	else
		result = FALSE;

	UnuseMemory(pg);
	
	return	result;
}


/* pgChangeParStyle is the same as pgChangeStyle except for paragraphs.  */

PG_PASCAL (void) pgChangeParStyle (pg_ref pg, short style_id, const par_info_ptr style,
		short draw_mode)
{
	paige_rec_ptr			pg_rec;
	par_info_ptr			the_style, each_style;
	par_info				old_style, mask;
	select_pair				range_affected;
	long					qty;
	short					negative_style_id, use_draw_mode;

	pg_rec = UseMemory(pg);

	if (the_style = pgLocateParStyleSheet(pg_rec, style_id)) {
		
		pgBlockMove(the_style, &old_style, SIGNIFICANT_PAR_STYLE_SIZE);
		pgBlockMove(style, the_style, SIGNIFICANT_PAR_STYLE_SIZE);
		the_style->style_sheet_id = style_id;

/* Now walk through all par_info's and locate any "offspring" from this
original stylesheet. If found, I need to change original fields. */
		
		each_style = UseMemoryRecord(pg_rec->par_formats, 0, USE_ALL_RECS, FALSE);
		negative_style_id = -style_id;

		for (qty = GetMemorySize(pg_rec->par_formats); qty; ++each_style, --qty) {
			
			if (each_style->style_sheet_id == negative_style_id) {
				
				pgFillBlock(&mask, SIGNIFICANT_PAR_STYLE_SIZE, COMPARE_TRUE);
				pgSetMaskFromFlds(&old_style, each_style, &mask, par_compare, FALSE);
				mask.style_sheet_id = 0;
				pgSetFldsFromMask(each_style, the_style, &mask, SIGNIFICANT_PAR_STYLE_SIZE);
			}
		}

		UnuseMemory(pg_rec->par_formats);
		
		++pg_rec->change_ctr;

		affected_text_range(pg_rec, par_compare, style_id, &range_affected);
		
		if (range_affected.end > range_affected.begin) {

			pgInvalSelect(pg, range_affected.begin, range_affected.end);

			if (use_draw_mode = draw_mode) {
				
				if (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);
}


/* pgSetParStyleSheet is the same as pgSetStyleSheet except for paragraphs.  */

PG_PASCAL (void) pgSetParStyleSheet (pg_ref pg, const select_pair_ptr selection,
		short style_id, short draw_mode)
{
	paige_rec_ptr			pg_rec;
	par_info_ptr			style_sheet;
	par_info				info, mask;

	pg_rec = UseMemory(pg);
	
	if (style_sheet = pgLocateParStyleSheet(pg_rec, style_id)) {

		pgBlockMove(style_sheet, &info, sizeof(par_info));
		UnuseMemory(pg_rec->par_formats);
		pgFillBlock(&mask, sizeof(par_info), -1);
		pg_rec->flags2 |= STYLESHEET_CHANGE;
		pgSetParInfo(pg, selection, &info, &mask, draw_mode);
		pg_rec->flags2 &= (~STYLESHEET_CHANGE);
	}

	UnuseMemory(pg);
}


/* pgGetParStyleSheet is the same as pgGetStyleSheet except for paragraphs.  */

PG_PASCAL (pg_boolean) pgGetParStyleSheet (pg_ref pg, const select_pair_ptr selection, short PG_FAR *style_id)
{
	par_info				info, mask;

	pgGetParInfo(pg, selection, FALSE, &info, &mask);

	if (info.style_sheet_id < 0)
		info.style_sheet_id = -info.style_sheet_id;

	if (style_id)
		*style_id = (short)info.style_sheet_id;
	
	return	(pg_boolean)(mask.style_sheet_id != 0);
}


/* pgFindParStyleSheet looks for any stylesheet that matches style. If found the
stylesheet ID is returned (otherwise 0 is returned).  */

PG_PASCAL (short) pgFindParStyleSheet (pg_ref pg, const par_info_ptr compare_style,
		const par_info_ptr mask)
{
	paige_rec_ptr				pg_rec;
	par_info					compare_mask;
	register par_info_ptr		target;
	register long				style_qty;
	short						result;

	pg_rec = UseMemory(pg);
	result = 0;
	
	if (mask)
		pgBlockMove(mask, &compare_mask, SIGNIFICANT_PAR_STYLE_SIZE);
	else
		pgFillBlock(&compare_mask, SIGNIFICANT_PAR_STYLE_SIZE, COMPARE_TRUE);
	
	compare_mask.style_sheet_id = 0;

	target = UseMemory(pg_rec->par_formats);
	
	for (style_qty = GetMemorySize(pg_rec->par_formats); style_qty; ++target, --style_qty)
		if (target->style_sheet_id > 0) {

			if (pgStyleMatchesCriteria(target, compare_style, &compare_mask,
					NULL, SIGNIFICANT_PAR_STYLE_SIZE)) {
				
				result = (short)target->style_sheet_id;
				break;
			}
		}

	UnuseMemory(pg_rec->par_formats);
	UnuseMemory(pg);

	return	result;
}

/* pgGetIndParStyleSheet returns the nth stylesheet's ID. If stylesheet ptr is not
NULL, it is filled in with the style. */


PG_PASCAL (short) pgGetIndParStyleSheet (pg_ref pg, short index, par_info_ptr stylesheet)
{
	paige_rec_ptr				pg_rec;
	register par_info_ptr		styles;
	register short				style_qty, ctr;
	short						result;

	pg_rec = UseMemory(pg);
	result = 0;

	for (styles = UseMemory(pg_rec->par_formats), style_qty = (pg_short_t)GetMemorySize(pg_rec->par_formats),
			ctr = 0; style_qty; ++styles, --style_qty)
		if (styles->style_sheet_id > 0) {
			
			if (ctr == index) {
				
				result = (short)styles->style_sheet_id;
				if (stylesheet)
					pgBlockMove(styles, stylesheet, sizeof(par_info));
				
				break;
			}

			++ctr;
		}

	UnuseMemory(pg_rec->par_formats);
	UnuseMemory(pg);
	
	return	result;
}


/* This is identical to pgLocateStyleSheet except for paragraphs.  */

PG_PASCAL (par_info_ptr) pgLocateParStyleSheet (paige_rec_ptr pg, short style_id)
{
	register par_info_ptr		styles;
	register pg_short_t			style_qty;

	if (!style_id)
		return	NULL;

	styles = UseMemory(pg->par_formats);

	for (style_qty = (pg_short_t)GetMemorySize(pg->par_formats); style_qty; ++styles, --style_qty)
		if (styles->style_sheet_id == style_id)
			return	styles;
	
	UnuseMemory(pg->par_formats);

	return	NULL;
}


/********** NAMED STYLES (2.0 implementation) ***********/

/* pgNewNamedStyle creates a new named stylesheet of stylename. If the name already exists, the
style(s) are changed to style, font and par.  The named style index is returned. */


PG_PASCAL (long) pgNewNamedStyle (pg_ref pg, pg_c_string_ptr stylename, const style_info_ptr style,
		const font_info_ptr font, par_info_ptr par)
{
	paige_rec_ptr			pg_rec;
	short					style_id, par_id;

	pg_rec = UseMemory(pg);
	
	if (style) {
		style_id = pgNewStyle(pg, style, font);
	}
	else
		style_id = 0;
	
	if (par)
		par_id = pgNewParStyle(pg, par);
	else
		par_id = 0;

	return pgAddNamedStyle(pg, stylename, style_id, par_id);
}


/* pgAddNamedStyle associates an existing stylesheet to a new named stylesheet of stylename. 
	If the name already exists, the style(s) are changed to style and par.  The named 
	style index is returned. */

PG_PASCAL (long) pgAddNamedStyle (pg_ref pg, pg_c_string_ptr stylename, const short style_id, const short par_id)
{
	paige_rec_ptr			pg_rec;
	named_stylesheet_ptr	named_style;
	style_info_ptr			style_ptr;
	par_info_ptr			par_ptr;
	long					style_index;
	short					name_index;

	pg_rec = UseMemory(pg);
	
	if ((style_index = pgGetNamedStyleIndex(pg, stylename)) > 0) {
		style_info_ptr		old_style;
		par_info_ptr		old_par;

		named_style = UseMemoryRecord(pg_rec->named_styles, style_index - 1, 0, TRUE);

		if (named_style->stylesheet_id)
			if (old_style = pgLocateStyleSheet(pg_rec, named_style->stylesheet_id, NULL)) {

				old_style->used_ctr -= 1;
				UnuseMemory(pg_rec->t_formats);
		}
		
		if (named_style->par_stylesheet_id)
			if (old_par = pgLocateParStyleSheet(pg_rec, named_style->par_stylesheet_id)) {

				old_par->used_ctr -= 1;
				UnuseMemory(pg_rec->par_formats);
		}
	}
	else {
		
		named_style = AppendMemory(pg_rec->named_styles, 1, TRUE);
		style_index = GetMemorySize(pg_rec->named_styles);
		
		for (name_index = 0; name_index < (FONT_SIZE - 1); ++name_index)
			if ((named_style->name[name_index] = stylename[name_index]) == 0)
				break;
	}
	
	if (style_id) {
	
		named_style->stylesheet_id = style_id;
		style_ptr = pgLocateStyleSheet(pg_rec, style_id, NULL);
		style_ptr->named_style_index = style_index;
		UnuseMemory(pg_rec->t_formats);
	}

	if (par_id) {
	
		named_style->par_stylesheet_id = par_id;
		par_ptr = pgLocateParStyleSheet(pg_rec, par_id);
		par_ptr->named_style_index = style_index;
		UnuseMemory(pg_rec->par_formats);
 	}
 
 	UnuseMemory(pg_rec->named_styles);
	UnuseMemory(pg);
	
	return		style_index;
}


/* pgApplyNamedStyle applies the named stylesheet to the specified selection. */

PG_PASCAL (void) pgApplyNamedStyle (pg_ref pg, select_pair_ptr selection, pg_char_ptr stylename, short draw_mode)
{
	long					style_index;
	
	if ((style_index = pgGetNamedStyleIndex(pg, stylename)))
		pgApplyNamedStyleIndex(pg, selection, style_index, draw_mode);
}


/* pgApplyNamedStyleIndex applies style index "index" to the specified selection. The whole
selection changes to the style and-or par format. */

PG_PASCAL (void) pgApplyNamedStyleIndex (pg_ref pg, select_pair_ptr selection, long index, short draw_mode)
{
	named_stylesheet		named_style;
	short					style_draw_mode;

	if (pgGetNamedStyle(pg, index, &named_style)) {
		
		if (!named_style.par_stylesheet_id)
			style_draw_mode = draw_mode;
		else
			style_draw_mode = draw_none;
		
		if (named_style.stylesheet_id)
			pgSetStyleSheet(pg, selection, named_style.stylesheet_id, style_draw_mode);
		if (named_style.par_stylesheet_id)
			pgSetParStyleSheet(pg, selection, named_style.par_stylesheet_id, draw_mode);
	}
}


/* pgNumNamedStyles returns the number of named stylesheets */

PG_PASCAL (long) pgNumNamedStyles (pg_ref pg)
{
	paige_rec_ptr			pg_rec;
	long					num_styles;
	
	pg_rec = UseMemory(pg);
	num_styles = GetMemorySize(pg_rec->named_styles);
	UnuseMemory(pg);
	
	return		num_styles;
}


/* pgGetNamedStyleIndex returns a named style index that matches stylename,
or zero if not found.  Or, if stylename is NULL the index, if any, is returned for
the current selection. */

PG_PASCAL (long) pgGetNamedStyleIndex (pg_ref pg, pg_c_string_ptr stylename)
{
	paige_rec_ptr					pg_rec;
	register named_stylesheet_ptr	named_style;
	long							index_result, num_styles, style_index;
	
	if (!stylename) {
		style_info			info, mask;
		par_info			parinfo, parmask;

		pgGetStyleInfo(pg, NULL, FALSE, &info, &mask);
		
		if (info.named_style_index == 0 || mask.named_style_index == 0) {
			
			info.named_style_index = 0;
			pgGetParInfo(pg, NULL, FALSE, &parinfo, &parmask);
			
			if (parmask.named_style_index != 0)
				info.named_style_index = parinfo.named_style_index;
		}
		
		return	info.named_style_index;
	}

	pg_rec = UseMemory(pg);
	num_styles = GetMemorySize(pg_rec->named_styles);
	named_style = UseMemory(pg_rec->named_styles);
	index_result = 0;

	for (style_index = 1; style_index <= num_styles; ++style_index, ++named_style) 
		if (compare_stylename(named_style->name, stylename)) {
		
		index_result = style_index;
		break;
	}

	UnuseMemory(pg_rec->named_styles);
	UnuseMemory(pg);

	return		index_result;
}


/* pgGetNamedStyleInfo returns the style, par and font_info records belonging to the
named style. */

PG_PASCAL (void) pgGetNamedStyleInfo (pg_ref pg, long index, style_info_ptr style,
		font_info_ptr font, par_info_ptr par)
{
	paige_rec_ptr			pg_rec;
	named_stylesheet_ptr	the_style;
	style_info_ptr			style_ptr;
	par_info_ptr			par_ptr;

	pg_rec = UseMemory(pg);
	
	if (index > 0 && index <= GetMemorySize(pg_rec->named_styles)) {
		
		the_style = UseMemoryRecord(pg_rec->named_styles, index - 1, 0, TRUE);
		
		if (style) {
			
			if ((style_ptr = pgLocateStyleSheet(pg_rec, the_style->stylesheet_id, NULL)) != NULL) {
				
				*style = *style_ptr;
				
				if (font)
					GetMemoryRecord(pg_rec->fonts, (long)style->font_index, font);

				UnuseMemory(pg_rec->t_formats);
			}
		}

		if (par) {
			
			if ((par_ptr = pgLocateParStyleSheet(pg_rec, the_style->par_stylesheet_id)) != NULL) {
				
				*par = *par_ptr;
				UnuseMemory(pg_rec->par_formats);
			}
		}

		UnuseMemory(pg_rec->named_styles);
	}

	UnuseMemory(pg);
}


/* pgGetNamedStyle returns the named_style_index.  If not a valid index, FALSE is returned. 
If named_style is non-NULL it is initialized with the named style, if found,  otherwise
the function is used to determine if the index is valid. */

PG_PASCAL (pg_boolean) pgGetNamedStyle (pg_ref pg, long named_style_index, named_stylesheet_ptr named_style)
{
	paige_rec_ptr			pg_rec;
	pg_boolean				valid_style;
	
	pg_rec = UseMemory(pg);
	valid_style = FALSE;
	
	if ( (named_style_index > 0) && (named_style_index <= GetMemorySize(pg_rec->named_styles)) ) {
		
		valid_style = TRUE;
		
		if (named_style)
			GetMemoryRecord(pg_rec->named_styles, named_style_index - 1, (void PG_FAR *)named_style);
	}

	UnuseMemory(pg);
	
	return		valid_style;
}


/* pgRenameStyle changes the name of the style index style to style_name. */

PG_PASCAL (void) pgRenameStyle (pg_ref pg, long named_style_index, pg_c_string_ptr style_name)
{
	paige_rec_ptr			pg_rec;
	named_stylesheet_ptr	the_style;
	short					ctr;

	pg_rec = UseMemory(pg);
	
	if ( (named_style_index > 0) && (named_style_index <= GetMemorySize(pg_rec->named_styles)) ) {
		
		the_style = UseMemoryRecord(pg_rec->named_styles, named_style_index - 1, 0, TRUE);
		pgFillBlock(the_style->name, FONT_SIZE * sizeof(pg_char), 0);
		
		for (ctr = 0; ctr < (FONT_SIZE) - 1; ++ctr)
			if ((the_style->name[ctr] = style_name[ctr]) == 0)
				break;
		
		the_style->name[ctr] = 0;
	}

	UnuseMemory(pg);
}


/* pgDeleteNamedStyle removes a named style from the list. */

PG_PASCAL (void) pgDeleteNamedStyle (pg_ref pg, long named_style_index)
{
	paige_rec_ptr			pg_rec;

	pg_rec = UseMemory(pg);
	
	if ( (named_style_index > 0) && (named_style_index <= GetMemorySize(pg_rec->named_styles)) ) {
		
		DeleteMemory(pg_rec->named_styles, named_style_index - 1, 1);
		
		if (pg_rec->def_named_index > (named_style_index - 1))
			--pg_rec->def_named_index;
	}

	UnuseMemory(pg);
}


/* pgSetDefaultNamedStyle sets the default named style (used mostly in RTF) */

PG_PASCAL (void) pgSetDefaultNamedStyle (pg_ref pg, long def_index)
{
	paige_rec_ptr			pg_rec;

	pg_rec = UseMemory(pg);
	
	if ((pg_rec->def_named_index = def_index - 1) < 0)
		pg_rec->def_named_index = 0;

	UnuseMemory(pg);
}

/* pgGetDefaultNamedStyle returns the default named style */

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

	pg_rec = UseMemory(pg);
	result = pg_rec->def_named_index;
	UnuseMemory(pg);

	return		result;
}


/* pgGetAppliedNamedStyle returns the named stylesheet applied to the specified selection.
If none found, FALSE is returned and named_style is set to an empty string. */

PG_PASCAL (pg_boolean) pgGetAppliedNamedStyle (pg_ref pg, select_pair_ptr selection, pg_c_string_ptr stylename)
{
	paige_rec_ptr			pg_rec;
	named_stylesheet_ptr	named_style;
	long					num_styles;
	pg_boolean				found;
	short					style_id, par_id, name_index;

	found = FALSE;

	if (!pgGetStyleSheet(pg, selection, &style_id))
		style_id = -1;
	if (!pgGetParStyleSheet(pg, selection, &par_id))
		par_id = -1;
	
	if (style_id > 0 || par_id > 0) {
	
		pg_rec = UseMemory(pg);
		named_style = UseMemory(pg_rec->named_styles);
		num_styles = GetMemorySize(pg_rec->named_styles);
		*stylename = 0;
	
		while (num_styles) {
			
			if (named_style->stylesheet_id == style_id && named_style->par_stylesheet_id == par_id) {
				
				for (name_index = 0; name_index < (FONT_SIZE - 1); ++name_index)
					if ((stylename[name_index] = named_style->name[name_index]) == 0)
						break;
				
				stylename[name_index] = 0;
				found = TRUE;
				break;
			}
			
			++named_style;
			--num_styles;
		}
		
		UnuseMemory(pg_rec->named_styles);
		UnuseMemory(pg);
	}
	
	return		found;
}


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


/* This function returns the total number of style sheets. If highest_id is
non-NULL, *highest_id returns with the highest ID found.  */

static short count_style_sheets (paige_rec_ptr pg, short PG_FAR *highest_id)
{
	register style_info_ptr		styles;
	register pg_short_t			style_qty;
	short						num_styles, highest;

	styles = UseMemory(pg->t_formats);
	style_qty = (pg_short_t)GetMemorySize(pg->t_formats);

	for (num_styles = highest = 0; style_qty; ++styles, --style_qty)
		if (styles->style_sheet_id > 0) {
		
		++num_styles;
		
		if (styles->style_sheet_id > highest)
			highest = (short)styles->style_sheet_id;
	}

	UnuseMemory(pg->t_formats);
	
	if (highest_id)
		*highest_id = highest;

	return		num_styles;
}


/* This is the same as count_style_sheets except for paragraphs.  */

static short count_par_style_sheets (paige_rec_ptr pg, short PG_FAR *highest_id)
{
	register par_info_ptr		styles;
	register pg_short_t			style_qty;
	short						num_styles, highest;

	styles = UseMemory(pg->par_formats);
	style_qty = (pg_short_t)GetMemorySize(pg->par_formats);

	for (num_styles = highest = 0; style_qty; ++styles, --style_qty)
		if (styles->style_sheet_id > 0) {
		
		++num_styles;
		
		if (styles->style_sheet_id > highest)
			highest = (short)styles->style_sheet_id;
	}

	UnuseMemory(pg->par_formats);
	
	if (highest_id)
		*highest_id = highest;

	return		num_styles;
}


/* Given a stylesheet change, this function returns the maximum text range
affected based on stylesheet_id. The compare_type is either style_compare or
par_compare. The range is returned in range->begin and range->end. */

static void affected_text_range (paige_rec_ptr pg, short compare_type,
		short stylesheet_id, select_pair_ptr range)
{
	register style_run_ptr			run;
	register select_pair_ptr		result;
	register long					qty;
	register short					index, style_id;
	short							negative_id;
	
	negative_id = -stylesheet_id;
	result = range;
	
	result->begin = pg->t_length;
	result->end = 0;

	if (compare_type == style_compare) {
		register style_info_ptr		base;

		run = UseMemory(pg->t_style_run);
		base = UseMemory(pg->t_formats);
		
		for (qty = GetMemorySize(pg->t_style_run) - 1; qty; ++run, --qty) {
			
			index = run->style_item;

			if (style_id = (short)base[index].style_sheet_id)
				if ((style_id == stylesheet_id) || (style_id == negative_id)) {
				
				if (run->offset < result->begin)
					result->begin = run->offset;
				if (run[1].offset > result->end)
					result->end = run[1].offset;
			}
		}

		UnuseMemory(pg->t_style_run);
		UnuseMemory(pg->t_formats);
	}
	else {
		register par_info_ptr		base;

		run = UseMemory(pg->par_style_run);
		base = UseMemory(pg->par_formats);
		
		for (qty = GetMemorySize(pg->par_style_run); qty; ++run, --qty) {
			
			index = run->style_item;

			if (style_id = (short)base[index].style_sheet_id)
				if ((style_id == stylesheet_id) || (style_id == negative_id)) {
				
				if (run->offset < result->begin)
					result->begin = run->offset;
				if (run[1].offset > result->end)
					result->end = run[1].offset;
			}
		}

		UnuseMemory(pg->par_style_run);
		UnuseMemory(pg->par_formats);
	}
}

/* compare_stylename returns TRUE if name1 matches name2. Both are cstrings. */

static pg_boolean compare_stylename (pg_c_string_ptr name1, pg_c_string_ptr name2)
{
	register pg_c_string_ptr		source_name;
	register pg_c_string_ptr		target_name;
	
	source_name = name1;
	target_name = name2;
	
	for (;;) {
		
		if (*source_name != *target_name)
			return	FALSE;
		if (*source_name++ == 0)
			break;
		
		++target_name;
	}
	
	return	TRUE;
}

