/* This is the PAIGE "insert anything" extension. This provides a slightly
higher-level interface to an otherwise complicated implementation of inserting
something that is not a real "text" item.  Copyright 1994-1995 by DataPak
Software, Inc.  All rights reserved. Software by "Gar."  */

/* 12/07/94 Corrected, improved and expanded by board member, TR Shaw, OITC. Expansions
   improvements and fixes are copyrighted 1994 OITC, Inc. */

/* 3/5/95 Made so that changes in the future will still support previous version - TRS/OITC */

//MAC NOTE: Need to change ControlRef to ControlHandle for Universal Interfaces before 2.0a3  ETO #16 TRS/OITC

#include "Paige.h"
#include "pgTraps.h"
#include "defprocs.h"
#include "pgUtils.h"
#include "pgDefStl.h"
#include "pgSelect.h"
#include "pgText.h"
#include "machine.h"
#include "pgFiles.h"
#include "pgEmbed.h"
#include "pgExceps.h"
#include "pgOSUtl.h"
#include "pgEdit.h"

#ifdef USING_GIF
#include "pgGIF.h"
#endif

#ifdef THINK_C
#define ControlRef ControlHandle
#endif

#define DROP_BOX_SIZE         2
#define DOUBLE_OFFSET         2


#ifndef WINDOWS_PLATFORM
#define MAKELONG(low, high) ((long)(((unsigned short)(low)) | (((unsigned long)((unsigned short)(high))) << 16)))
#endif

#ifndef MAC_PLATFORM
struct Mac_Picture {
   short                picSize;
   Rect                 picFrame;
};
typedef struct Mac_Picture Mac_Picture;
typedef Mac_Picture PG_FAR *Mac_PicPtr;
#endif


#define MAC_CONTROL_STR    "Mac"
#define OLE_STR            "OLE"
#define MAC_PICTURE_STR    "Pict"
#define  MAC_QT_STR        "QT"
#define WIN_META_STR    "Meta"
#define OS2_META_STR    "Meta"
#define WIN_BMP_STR        "bmp"
#define WIN_DDBMP_STR      "ddbmp"
#define UNKNOWN_STR        "Unkn"

static void initialize_embed_data (pgm_globals_ptr mem_globals, embed_ref the_ref,
      void PG_FAR *data, pg_fixed vert_pos);
static long setup_for_new_ref (paige_rec_ptr pg, long position, embed_ref ref,
   short stylesheet_option, embed_callback callback, long callback_refcon,
   style_info_ptr style, style_info_ptr mask, font_info_ptr font,
   font_info_ptr font_mask, pg_boolean will_be_inserted);
static pg_boolean next_style_same_ref (paige_rec_ptr pg, style_walk_ptr walker);
static pg_boolean previous_style_same_ref (paige_rec_ptr pg, style_walk_ptr walker);
static long extra_trailing_spaces (paige_rec_ptr pg, style_walk_ptr walker,
      pg_char_ptr text, long length);
static pg_boolean have_saved_ref (style_info_ptr stylebase, short current_index);
static void pack_embed_ref (pack_walk_ptr walker, embed_ref ref);
static void pack_pic_embed(pack_walk_ptr walker, pg_embed_ptr embed_ptr);
static void unpack_pic_embed(pack_walk_ptr walker, pg_embed_ptr embed_ptr);
static co_ordinate_ptr center_unknown_text(pg_embed_ptr item, graf_device_ptr port, pg_char_ptr text, short length, rectangle_ptr draw_bounds, co_ordinate_ptr top_left_pt);
static void set_string_data (pgm_globals_ptr mem_globals, pg_embed_ptr embed_ptr, long the_type,
      pg_char_ptr alt_char_ptr, short str_size);
static void get_visual_frame (paige_rec_ptr pg, pg_embed_ptr embed_ptr,
      select_pair_ptr range, rectangle_ptr bounds);
static void unpack_altsize_text (paige_rec_ptr pg, pack_walk_ptr walker, pg_char_ptr str);

static void standard_embed_draw (paige_rec_ptr pg, pg_embed_ptr item,
      rectangle_ptr draw_bounds, draw_points_ptr draw_position, pg_boolean drawing_to_bitmap);
static void standard_embed_dispose (pg_embed_ptr item, long the_type);
static long standard_embed_click (paige_rec_ptr pg, pg_embed_ptr item,
      long item_type, pg_embed_click_ptr click_info);
static void custom_data_callback (paige_rec_ptr pg, pack_walk_ptr walker, pg_embed_ptr embed_ptr,
      embed_callback callback, memory_ref buffer_ref);
static pg_boolean embed_empty (pg_embed_ptr embed_ptr);


STATIC_PASCAL (void) measure_embed (paige_rec_ptr pg, style_walk_ptr walker,
      pg_char_ptr data, long length, pg_short_t slop, long PG_FAR *positions,
      short PG_FAR *types, short measure_verb, long current_offset, short scale_widths,
      short call_order);
STATIC_PASCAL (long) track_embed_ctl (paige_rec_ptr pg, short verb,
      t_select_ptr first_select, t_select_ptr last_select, style_walk_ptr styles,
      pg_char_ptr associated_text, point_start_ptr bounds_info, short modifiers,
      long track_refcon);
STATIC_PASCAL (void) embed_draw (paige_rec_ptr pg, style_walk_ptr walker, pg_char_ptr data,
      pg_short_t offset, pg_short_t length, draw_points_ptr draw_position,
      long extra, short draw_mode);
STATIC_PASCAL (void) embed_copy (paige_rec_ptr src_pg, paige_rec_ptr target_pg,
      short reason_verb, format_ref all_styles, style_info_ptr style);
STATIC_PASCAL (void) embed_delete (paige_rec_ptr pg, pg_globals_ptr globals,
      short reason_verb, format_ref all_styles, style_info_ptr style);
STATIC_PASCAL (pg_boolean) embed_setup_insert_init (paige_rec_ptr pg, style_info_ptr style,
      long position);
STATIC_PASCAL (pg_boolean) embed_setup_insert (paige_rec_ptr pg, style_info_ptr style,
      long position);
STATIC_PASCAL (long) embed_char_info (paige_rec_ptr pg, style_walk_ptr style_walker,
      pg_char_ptr data, long block_offset, long offset_begin, long offset_end, long char_offset, long mask_bits);
STATIC_PASCAL (void) embed_activate (paige_rec_ptr pg_rec, style_info_ptr style,
      select_pair_ptr text_range, pg_char_ptr text, short front_back_state,
      short perm_state, pg_boolean show_hilite);

#ifdef WINDOWS_PLATFORM
static void draw_meta_file (paige_rec_ptr pg, HDC target_dc, pg_embed_ptr embed_ptr, RECT PG_FAR *target_rect);
#endif

#ifdef MAC_PLATFORM

#include <Controls.h>
#include <Windows.h>
static void detach_control_from_window (ControlHandle ctl);

#endif



/* pgNewEmbedRef creates a new embedding reference. It does not get inserted
into anything, but you can keep it around and insert it into a pg_ref (or
apply it to already-existing bytes in a pg_ref) later. If keep_around is TRUE
the object will not get disposed when embedded in the text stream, i.e. you
want to always have the object around in your app even if it is inserted into
text then deleted.
The modifier parameter value is intepreted differently for various embed types
as follows:  if type is character substitution, date-time swap or page numbering
type (or any other type that draws text), modifier is an alternate stylesheet
ID (or zero if none), which gets set when the item is drawn and measured. If
type is a rounded rectangle or arc, modifier is the angle amount.  Currently
all other types ignore the modifier.
The user_refcon value is placed in the embed_ref. Note, this value is different
than the "style user_refcon" since the style refcon is unique to each style,
given at the moment of pgInsertEmbed.  */


PG_PASCAL (embed_ref) pgNewEmbedRef (pgm_globals_ptr mem_globals, long item_type,
      void PG_FAR *item_data, long modifier, long flags, pg_fixed vert_pos,
      long user_refcon, pg_boolean keep_around)
{
   embed_ref         result;
   pg_embed_ptr      embed_ptr;
   long           	 old_current_id;
   
   old_current_id = mem_globals->current_id;
   ++mem_globals->next_mem_id;
   mem_globals->current_id = mem_globals->next_mem_id;

   result = MemoryAllocClear(mem_globals, sizeof(pg_embed_rec), 1, 0);
   embed_ptr = UseMemory(result);

   embed_ptr->version = CURRENT_EMBED_VERSION;
   embed_ptr->type = item_type;
   embed_ptr->type |= flags;

   if (keep_around) {
   
      embed_ptr->used_ctr = 1;
      embed_ptr->type |= KEPT_AROUND;
   }
   
   embed_ptr->user_refcon = user_refcon;
   embed_ptr->modifier = modifier;

   PG_TRY (mem_globals)
   {
      initialize_embed_data(mem_globals, result, item_data, vert_pos);
   }
   PG_CATCH
   {
      UnuseMemory(result);
      pgEmbedDispose (result);
      mem_globals->current_id = old_current_id;
      PG_RERAISE();
   }
   PG_ENDTRY

   UnuseMemory(result);
   mem_globals->current_id = old_current_id;

   return   result;
}


/* pgEmbedDispose disposes the embed_ref. This is only called if "keep_around"
was TRUE when the embed_ref was created. */

PG_PASCAL (void) pgEmbedDispose (embed_ref ref)
{
   pg_embed_ptr         embed_ptr;
   
   embed_ptr = UseMemory(ref);
   pgDefaultEmbedCallback((paige_rec_ptr)NULL, embed_ptr, embed_ptr->type & EMBED_TYPE_MASK,
         EMBED_DESTROY, 0, 0, 0);

   UnuseAndDispose(ref);
}


/* pgGetExistingEmbed locates the embed_ref, if any, attached to character(s)
that contains user_refcon value (the original user value given in pgNewEmbedRef).  */

PG_PASCAL (embed_ref) pgGetExistingEmbed (pg_ref pg, long user_refcon)
{
   paige_rec_ptr              pg_rec;
   register style_info_ptr       styles;
   embed_ref                  result;
   long                    num_styles;

   pg_rec = UseMemory(pg);
   result = MEM_NULL;
   num_styles = GetMemorySize(pg_rec->t_formats);
   
   for (styles = UseMemory(pg_rec->t_formats); num_styles; ++styles, --num_styles)
      if (styles->embed_object && (styles->embed_refcon == user_refcon)) {
            result = styles->embed_object;
            break;
      }

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

   return   result;
}


/* pgFindNextEmbed searches all embed refs in the document, beginning with (and including)
*text_position. If AND_refcon is non-zero, its bits are AND'd with the style_refcon of each
one and compared to match_refcon. If AND_refcon is zero or the resulting comparison with
match_refcon is TRUE, the embed_ref is returned and *text_position is updated. If nonne
found, MEM_NULL is returned and *text_position will be end of text. */

PG_PASCAL (embed_ref) pgFindNextEmbed (pg_ref pg, long PG_FAR *text_position,
      long match_refcon, long AND_refcon)
{
   paige_rec_ptr              pg_rec;
   register style_info_ptr    stylebase;
   style_info_ptr             style;
   style_run_ptr              run;
   pg_embed_ptr               embed_ptr;
   embed_ref                  result;
   pg_short_t                 style_index;
   long                    first_run, num_styles;

   pg_rec = UseMemory(pg);
   result = MEM_NULL;
   
   if (*text_position < 0)
   		*text_position = pgFixOffset(pg_rec, *text_position);
   
   if (*text_position >= pg_rec->t_length) {
   
   		UnuseMemory(pg);
   		return	MEM_NULL;
   }

   num_styles = GetMemorySize(pg_rec->t_style_run) - 1;
   run = pgFindStyleRun(pg_rec, *text_position, &first_run);
   num_styles -= (long)first_run;
   stylebase = UseMemory(pg_rec->t_formats);

   while (num_styles) {
      
      style_index = run->style_item;
      style = &stylebase[style_index];
      
      if (style->embed_object)
         if (!AND_refcon || ((style->embed_style_refcon & AND_refcon) == match_refcon)) {
            
            result = style->embed_object;
            embed_ptr = UseMemory(result);
            embed_ptr->style_refcon = style->embed_style_refcon;
            embed_ptr->lowlevel_index = (long)style_index;
            UnuseMemory(result);

            break;
         }
      
      ++run;
      --num_styles;
   }
 
   if (text_position) {
   
      if (result == MEM_NULL)
         *text_position = pg_rec->t_length;
      else
         *text_position = run->offset;
   }

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

   return   result;
}



/* pgInsertEmbedRef inserts an embedded object into the text stream. This
objects must have been previously created with pgNewEmbedRef as above (or
created descretely if the creator knew what to do). The style_refcon is an
additional value that the app can set for this particular style. */

PG_PASCAL (pg_boolean) pgInsertEmbedRef (pg_ref pg, embed_ref ref, long position,
      short stylesheet_option, embed_callback callback, long callback_refcon,
      short draw_mode)
{
   paige_rec_ptr        pg_rec;
   style_info           new_style, mask;
   font_info            font, font_mask;
   pg_embed_ptr         embed_ptr;
   long                 the_type, type_and_flags, current_pos;
   pg_char              dummy_insert[2];
   pg_boolean           result;

   pg_rec = UseMemory(pg);
   
   if (pg_rec->num_selects)
      pgDelete(pg, NULL, draw_none);
   
   embed_ptr = UseMemory(ref);
   type_and_flags = embed_ptr->type;
   the_type = type_and_flags & EMBED_TYPE_MASK;

   UnuseMemory(ref);
   
   pgInitEmbedStyleInfo(pg_rec, position, ref, stylesheet_option, callback, callback_refcon,
         &new_style, &mask, &font, &font_mask, TRUE);

   pgSetFontInfo(pg, NULL, &font, &font_mask, draw_none);
   pgSetStyleInfo(pg, NULL, &new_style, &mask, draw_none);
   current_pos = pgCurrentInsertion(pg_rec);
   pgInvalTextMeasure(pg_rec,  current_pos, current_pos + 2);

   dummy_insert[0] = DUMMY_LEFT_EMBED;
   dummy_insert[1] = DUMMY_RIGHT_EMBED;

   result = (pgInsert(pg, dummy_insert, 2, CURRENT_POSITION, data_insert_mode, 0, draw_mode));

   UnuseMemory(pg);

   return   result;
}


/* pgSetEmbedRef should be used INSTEAD OF pgInsertEmbedRef when you want to
apply some embed info to bytes that already exist in the text. The selection
parameter indicates the byte range within pg to apply the embed_ref, or if
it is NULL then the current selection range is used. The callback_refcon can
be anything. Attributes will change the applied "style" in several ways (** for
this version, pass zero **).
NOTE, the applied reference must be for ONE LOGICAL CHARACTER ONLY. */

PG_PASCAL (void) pgSetEmbedRef (pg_ref pg, embed_ref ref, select_pair_ptr selection,
      short stylesheet_option, embed_callback callback, long callback_refcon,
      short draw_mode)
{
   paige_rec_ptr        pg_rec;
   select_pair          change_range;
   style_info           new_style, mask;
   long              new_type;

   pg_rec = UseMemory(pg);
   
   if (selection)
      change_range = *selection;
   else
      pgGetSelection(pg, &change_range.begin, &change_range.end);

   new_type = pgInitEmbedStyleInfo(pg_rec, change_range.begin, ref, stylesheet_option,
         callback, callback_refcon, &new_style, &mask, NULL, NULL, FALSE);

   UnuseMemory(pg);
   
   if (new_style.char_bytes = (short)(change_range.end - change_range.begin))
      --new_style.char_bytes;

   pgSetStyleInfo(pg, &change_range, &new_style, &mask, draw_mode);
}



/* pgNumEmbeds returns the numger of embedded references in selection range
(or the current selection point or range if selection is NULL). */

PG_PASCAL (long) pgNumEmbeds (pg_ref pg, select_pair_ptr selection)
{
   paige_rec_ptr        pg_rec;
   register style_info_ptr style_base;
   select_pair          use_to_init;
   select_pair_ptr         selections;
   style_run_ptr        run;
   memory_ref           select_ref;
   long              result, num_selects;
   
   pg_rec = UseMemory(pg);
   result = 0;
   
   if (selection)
      use_to_init = *selection;
   else
   if (pg_rec->num_selects)
   		pgGetSelection(pg, &use_to_init.begin, &use_to_init.end);
   else {
      
      use_to_init.begin = 0;
      use_to_init.end = pg_rec->t_length;
   }

   if (select_ref = pgSetupOffsetRun(pg_rec, &use_to_init, FALSE, FALSE)) {
      
      num_selects = GetMemorySize(select_ref);
      selections = UseMemory(select_ref);
      style_base = UseMemory(pg_rec->t_formats);

      while (num_selects) {
         
         run = pgFindStyleRun(pg_rec, selections->begin, NULL);
         
         while (run->offset < selections->end) {
            
            if (style_base[run->style_item].embed_object)
               ++result;
            
            ++run;
         }

         UnuseMemory(pg_rec->t_style_run);
         
         ++selections;
         --num_selects;
      }
      
      UnuseMemory(pg_rec->t_formats);
      UnuseAndDispose(select_ref);
   }
   else {
      style_info           single_style, mask;
      
      pgGetStyleInfo(pg, NULL, FALSE, &single_style, &mask);
      
      if (single_style.embed_object)
         ++result;
   }

   UnuseMemory(pg);
   
   return   result;
}


/* pgGetIndEmbed returns the nth embedded reference, 1-based, found within
the selection (or if selection is NULL, the current selection). If none are
found, MEM_NULL is returned. The text_position and associated_style params are optional: if
text_position is non-NULL, the first char position of the embed_ref is returned. If
associated_style is non-NULL, a copy of the associated style is returned. */

PG_PASCAL (embed_ref) pgGetIndEmbed (pg_ref pg, select_pair_ptr selection, long index,
      long PG_FAR *text_position, style_info_ptr associated_style)
{
   paige_rec_ptr        pg_rec;
   register style_info_ptr style_base;
   select_pair_ptr         selections;
   style_run_ptr        run;
   memory_ref           select_ref;
   embed_ref            result;
   long              num_selects, index_ctr;
   
   if (!index)
      return   MEM_NULL;

   pg_rec = UseMemory(pg);
   result = MEM_NULL;
   index_ctr = 0;

   if (select_ref = pgSetupOffsetRun(pg_rec, selection, FALSE, FALSE)) {
      
      num_selects = GetMemorySize(select_ref);
      selections = UseMemory(select_ref);
      style_base = UseMemory(pg_rec->t_formats);

      while (num_selects) {
         
         run = pgFindStyleRun(pg_rec, selections->begin, NULL);
         
         while (run->offset < selections->end) {
            
            if (style_base[run->style_item].embed_object) {
               
               ++index_ctr;
               
               if (index_ctr == index) {
                  
                  result = style_base[run->style_item].embed_object;
                  
                  if (text_position)
                     *text_position = run->offset;
                  if (associated_style)
                     pgBlockMove(&style_base[run->style_item], associated_style, sizeof(style_info));

                  break;
               }
            }
            
            ++run;
         }

         UnuseMemory(pg_rec->t_style_run);
         
         if (result)
            break;

         ++selections;
         --num_selects;
      }

      UnuseMemory(pg_rec->t_formats);
      UnuseAndDispose(select_ref);
   }
   else {
      style_info           single_style, mask;
      
      if (index == 1) {
      
         pgGetStyleInfo(pg, NULL, FALSE, &single_style, &mask);
         
         if (result = single_style.embed_object) {

            if (text_position)
               *text_position = pgCurrentInsertion(pg_rec);
            if (associated_style)
               pgBlockMove(&single_style, associated_style, sizeof(style_info));
         }
      }
   }

   UnuseMemory(pg);
   
   return   result;
}


/* pgPtInEmbed returns the embed_ref, if any, that contains point. If text_position is non-NULL,
the first textoffset is returned. If associated_style is non-NULL, the associated style_info
is returned. If do_callback is TRUE and a valid embed_ref
is located containing point, the callback function is given EMBED_CURSOR. */

PG_PASCAL (embed_ref) pgPtInEmbed (pg_ref pg, co_ordinate_ptr point, long PG_FAR *text_offset,
      style_info_ptr associated_style, pg_boolean do_callback)
{
   paige_rec_ptr        pg_rec;
   style_info           the_style;
   select_pair          the_range;
   pg_embed_ptr         embed_ptr;
   rectangle_ptr        bounds_ptr;
   rectangle            bounds;
   embed_callback       callback;
   embed_ref            ref, result;
   long              embed_type;

   result = MEM_NULL;

   pgPtToStyleInfo(pg, point, NO_BYTE_ALIGN | NO_HALFCHARS, &the_style, &the_range);
   
   if (ref = (embed_ref)the_style.embed_object) {
      
      result = ref;
      
      if (text_offset)
         *text_offset = the_range.begin;
      if (associated_style)
         pgBlockMove(&the_style, associated_style, sizeof(style_info));

      pg_rec = UseMemory(pg);
      embed_ptr = UseMemory(ref);
      
      get_visual_frame(pg_rec, embed_ptr, &the_range, &bounds);

      embed_type = embed_ptr->type & EMBED_TYPE_MASK;

      { // PDA:  This fixes a scaling bug!!!!!!
         co_ordinate offset;
         pg_scale_factor scaling;
         
         scaling = pg_rec->port.scale;
         scaling.scale = -scaling.scale;
         offset = pg_rec->scroll_pos;
         pgNegatePt(&offset);
         pgScalePt(&scaling, &offset, point);
      } // End of fix!!!!

      if (pgPtInRect(point, &bounds)) {
      
         if (do_callback) {
            
            callback = (embed_callback)the_style.embed_entry;
            bounds_ptr = &bounds;
            embed_ptr->style = &the_style;
            callback(pg_rec, embed_ptr, embed_type, EMBED_CURSOR,
                  the_style.embed_style_refcon, 
                  (long)point, (long)bounds_ptr);
            embed_ptr->style = NULL;
         }
      }
      else
         result = MEM_NULL;

      UnuseMemory(pg);
      UnuseMemory(ref);
   }

   return   result;
}


/* pgEmbedStyleToIndex returns the index number (1-based) of the embed_ref attached to
embed_style. If none, zero is returned. */

PG_PASCAL (long) pgEmbedStyleToIndex (pg_ref pg, style_info_ptr embed_style)
{
   paige_rec_ptr           pg_rec;
   register style_info_ptr    stylebase;
   style_info_ptr          style;
   style_run_ptr           run;
   long                 num_runs, index, embed_id;

   index = 0;

   if (embed_style->embed_object) {
      
      pg_rec = UseMemory(pg);
      stylebase = UseMemory(pg_rec->t_formats);
      run = UseMemory(pg_rec->t_style_run);
      num_runs = GetMemorySize(pg_rec->t_style_run) - 1;
      embed_id = embed_style->embed_id;

      while (num_runs) {
         
         style = &stylebase[run->style_item];
         
         if (style->embed_object) {

            ++index;
   
            if (style->embed_id == embed_id)
               break;
         }

         ++run;
         --num_runs;
      }
      
      UnuseMemory(pg_rec->t_style_run);
      UnuseMemory(pg_rec->t_formats);
      UnuseMemory(pg);
   }
   
   return   index;
}



/* pgGetEmbedJustClicked is typically called after pgDragSelect returns a
non-zero number which, if track_refcon in pgDragSelect was originally zero,
implies that an embedded item was just clicked.  This function will find that
embed_ref and return it.  If not found for some reason, MEM_NULL is the result. */

PG_PASCAL (embed_ref) pgGetEmbedJustClicked (pg_ref pg, long drag_select_result)
{
   paige_rec_ptr              pg_rec;
   register style_info_ptr       styles;
   embed_ref                  result;
   long                    num_styles;
   
   pg_rec = UseMemory(pg);
   result = MEM_NULL;

   num_styles = GetMemorySize(pg_rec->t_formats);
   for (styles = UseMemory(pg_rec->t_formats); num_styles; ++styles, --num_styles)
      if (styles->embed_id == drag_select_result) {
         
         result = styles->embed_object;
         break;
      }
   
   UnuseMemory(pg_rec->t_formats);
   UnuseMemory(pg);
   
   return      result;
}


/* pgGetEmbedBounds returns the bounding box for embed_ref represented by index. The bounds
result is scaled (if scaling enabled) but not scrolled -- scrolled position, if any, is
given in screen_extra. The amount the rectangle is offset from the basline of the text is
given in vertical_pos. Note, any one of these pointers can be NULL. The function returns the
text position of the embed_ref. */

PG_PASCAL (long) pgGetEmbedBounds (pg_ref pg, long index, select_pair_ptr index_range,
      rectangle_ptr bounds, short PG_FAR *vertical_pos, co_ordinate_ptr screen_extra)
{
   paige_rec_ptr           pg_rec;
   pg_embed_ptr            embed_ptr;
   style_info              style;
   select_pair             style_range;
   embed_ref               ref;
   long                 result;

   result = -1;

   if (ref = pgGetIndEmbed(pg, index_range, index, &style_range.begin, &style)) {

      pg_rec = UseMemory(pg);
      embed_ptr = UseMemory(ref);
      result = style_range.begin;
      style_range.end = style_range.begin + (style.char_bytes + 1);
      
      if (bounds)
         get_visual_frame(pg_rec, embed_ptr, &style_range, bounds);
      if (vertical_pos)
         *vertical_pos = (short)embed_ptr->descent;
      if (screen_extra) {
         
         *screen_extra = pg_rec->scroll_pos;
         pgNegatePt(screen_extra);
         pgAddPt(&pg_rec->port.origin, screen_extra);
      }

      UnuseMemory(ref);
      UnuseMemory(pg);
   }
   
   return   result;
}


/* pgSetEmbedBounds changes the physical bounds of the embed_ref defined by index against
index_range. If bounds is non-NULL the embed_ref gets set to that width and height.
If vertical_pos is non-null the descent is set to that value. */

PG_PASCAL (void) pgSetEmbedBounds (pg_ref pg, long index, select_pair_ptr index_range,
		rectangle_ptr bounds, short PG_FAR *vertical_pos, short draw_mode)
{
   paige_rec_ptr           pg_rec;
   pg_embed_ptr            embed_ptr;
   long					   position, new_width, new_height, new_descent;
   short				   use_draw_mode;
   embed_ref               ref;

   if (ref = pgGetIndEmbed(pg, index_range, index, &position, NULL)) {

	  pg_rec = UseMemory(pg);
	  embed_ptr = UseMemory(ref);
	  
	  if (bounds) {
	  
		  new_width = bounds->bot_right.h - bounds->top_left.h;
		  new_height = bounds->bot_right.v - bounds->top_left.v;
	  }
	  else {
	  
	  	  new_width = embed_ptr->width;
	  	  new_height = embed_ptr->height;
	  }
	  
	  if (vertical_pos)
	  	  new_descent = *vertical_pos;
	  else
	  	  new_descent = embed_ptr->descent;

	  if (embed_ptr->width != new_width || embed_ptr->height != new_height
	  		|| embed_ptr->descent != new_descent) {
	  		style_run_ptr		run;
	  		style_info_ptr		style;
	  		
		  	pgInvalSelect(pg, position, position + 2);
			embed_ptr->width = new_width;
			embed_ptr->height = new_height;
			embed_ptr->descent = new_descent;
			
			run = UseMemory(pg_rec->t_style_run);
			
			while (run->offset <= position) {
				
				if (run->offset == position) {
					
					style = UseMemoryRecord(pg_rec->t_formats, (long)run->style_item, 0, TRUE);
					style->descent = (short)embed_ptr->descent;
					style->ascent = (short)(embed_ptr->height - embed_ptr->descent);
					UnuseMemory(pg_rec->t_formats);
					break;
				}
				
				++run;
			}

			UnuseMemory(pg_rec->t_style_run);
			
			if (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(ref);
	  UnuseMemory(pg);
   }
}


/* pgDrawAlternateText draws the alternate char-swap text in item to pin to
top-left of draw_bounds. If optional_pointsize is non-zero the size is
temporarily set to that point size. */

PG_PASCAL (void) pgDrawAlternateText (graf_device_ptr device, pg_embed_ptr item,
      rectangle_ptr draw_bounds, short optional_pointsize)
{
   pg_char_ptr       the_chars;
   pg_short_t        the_size;
   
   if (item->data) {
      
      the_chars = UseMemory((memory_ref)item->data);
      the_size = (pg_short_t)GetMemorySize((memory_ref)item->data);
   }
   else {
      the_chars = (pg_char_ptr)&item->uu.alt_data;
      the_size = (pg_short_t)pgCStrLength((pg_c_string_ptr)the_chars);
   }

   if (the_size)
      pgTextOut(device, &draw_bounds->top_left, the_chars, the_size, optional_pointsize);

   if (item->data)
      UnuseMemory((memory_ref)item->data);
}



/* pgSetEmbedBorders adjusts the offsets due to borders and sets the border information */

PG_PASCAL (void) pgSetEmbedBorders (embed_ref ref, pg_char type, pg_char style, color_value_ptr color, short pen_size)
{
   pg_embed_ptr   embed;
   short       size;
   
   embed = UseMemory(ref);
   
   embed->border.type = type;
   embed->border.style = style;
   if (color)
      embed->border.color = *color;
   else pgFillBlock(&embed->border.color, sizeof(color_value), 0); 
   
   size = pen_size;

   if (style == border_double)
   {
         size <<= 2;
   }
   
   embed->border.pen_size = size;
   
   switch (type)
   {
      default:
      case border_none:
         break;

      case border_box:
         if (style == border_shadow)
         {
            embed->bot_extra += DROP_BOX_SIZE;
            embed->right_extra += DROP_BOX_SIZE;
         }
         embed->left_extra += size;
         embed->right_extra += size;
         embed->bot_extra += size;
         
      case border_top:
         embed->top_extra += size;
         break;
         
      case border_bottom:
         embed->bot_extra += size;
         break;
         
      case border_left:
         embed->left_extra += size;
         break;
         
      case border_right:
         embed->right_extra += size;
         break;
   }
   UnuseMemory(ref);
}
   

/* pgDrawEmbedBorders is also available by user of embededs.  It draws bounding boxes */
//¥ Color and pattern still to do

PG_PASCAL (void) pgDrawEmbedBorders(pg_globals_ptr globals, graf_device_ptr target_device, pg_embed_ptr embed, rectangle_ptr draw_bounds)
{
   co_ordinate start_pt;
   co_ordinate end_pt;
   short    pen_size = embed->border.pen_size;
   
   switch (embed->border.type)
   {
      case border_none:
         return;
         
      case border_box:
         pgFrameObject (target_device, draw_bounds, 0, object_rect, pen_size);

         if (embed->border.style != border_shadow)
            return;
         
         pen_size = DROP_BOX_SIZE;
         
         start_pt.v = draw_bounds->top_left.v - DROP_BOX_SIZE;
         end_pt.h = start_pt.h = draw_bounds->top_left.h + 1;
         end_pt.v = draw_bounds->bot_right.v;
         pgLineDraw (target_device, &start_pt, &end_pt, pen_size);
         
         start_pt.h = draw_bounds->top_left.h - DROP_BOX_SIZE;
         end_pt.v = start_pt.v = draw_bounds->bot_right.v + 1;
         end_pt.h = draw_bounds->bot_right.h;
         break;
         
      case border_top:
         start_pt.v = end_pt.v = draw_bounds->top_left.v;
         start_pt.h = draw_bounds->top_left.h;
         end_pt.h = draw_bounds->bot_right.h;
         break;
         
      case border_bottom:
         start_pt.h = draw_bounds->top_left.h;
         end_pt.v = start_pt.v = draw_bounds->bot_right.v;
         end_pt.h = draw_bounds->bot_right.h;
         break;
         
      case border_left:
         start_pt.v = draw_bounds->top_left.v;
         start_pt.h = end_pt.h = draw_bounds->top_left.h;
         end_pt.v = draw_bounds->bot_right.v;
         break;
         
      case border_right:
         start_pt.h = draw_bounds->top_left.h;
         end_pt.v = start_pt.v = draw_bounds->bot_right.v;
         end_pt.h = draw_bounds->bot_right.h;
         break;
   }

   pgLineDraw (target_device, &start_pt, &end_pt, pen_size);
}


/* pgDefaultEmbedCallback is the default callback function for embed_refs for
which application specified NULL callback. Or, if application does not know
how to handle the command it should call this directly. */

PG_PASCAL (long) pgDefaultEmbedCallback (paige_rec_ptr pg, pg_embed_ptr embed_ptr,
      long embed_type, short command, long user_refcon,
      long param1, long param2)
{
   long     result = 0;

   switch (command) {

      case EMBED_DRAW:
         standard_embed_draw(pg, embed_ptr, (rectangle_ptr)param1, (draw_points_ptr)param2,
               (pg_boolean)(pg->globals->offscreen_enable == OFFSCREEN_SUCCESSFUL));
         break;

      case EMBED_MOUSEDOWN:
         result = standard_embed_click(pg, embed_ptr, embed_type,
               (pg_embed_click_ptr) param1);
         break;
         
      case EMBED_MOUSEMOVE:
      case EMBED_MOUSEUP:
      case EMBED_DOUBLECLICK:
         result = embed_ptr->style->embed_id;
         break;

      case EMBED_DESTROY:
         standard_embed_dispose(embed_ptr, embed_type);
         break;

      case EMBED_SWAP:
         if ((result = pgCStrLength((pg_c_string_ptr)param1)) > param2)
            result = param2;
         break;
      
      case EMBED_INIT:
         break;
      
      case EMBED_CURSOR:
         break;
      
      case EMBED_ACTIVATE:
         break;
      
      case EMBED_DEACTIVATE:
         break;
      
      case EMBED_WRITE_DATA:
         break;
      
      case EMBED_READ_DATA:
         break;
   }
   
   return   result;
}



/* pgEmbedReadHandler is the standard handler function for reading back embed_refs
from a PAIGE file. */

PG_PASCAL (pg_boolean) pgEmbedReadHandler (paige_rec_ptr pg, pg_file_key key, memory_ref key_data,
      long PG_FAR *element_info, void PG_FAR *aux_data, long PG_FAR *unpacked_size)
{
   pg_embed_ptr           	  	embed_ptr;
   pgm_globals_ptr            	mem_globals;
   register style_info_ptr    	styles;
   pg_bits8_ptr             	custom_data;
   embed_ref               		ref;
   pack_walk               		walker;
   embed_callback          		callback;
   long                 		num_styles, ref_id, the_type, input_byte_size;
   long                 		called_ctr, custom_size;
   long                 		version, file_version, old_current_id;

   file_version = pg->version;

   pgSetupPacker(&walker, key_data, 0);
   mem_globals = pg->globals->mem_globals;

   ref_id = pgUnpackNum(&walker);      // Was original memory_ref value
   
   if (file_version > KEY_REVISION6)
      version = pgUnpackNum(&walker);     // Get the version so we can be compatible in the future
   else version = 1;
   
   the_type = pgUnpackNum(&walker);
   
   ref = pgNewEmbedRef(mem_globals, the_type, NULL, 0, 0, 0, 0, FALSE);

// Bug fix 8/16/95, any data struct within embed_ref must have different ID than pg_ref:

   old_current_id = mem_globals->current_id;
   mem_globals->current_id = GetMemoryRefID(ref);
   embed_ptr = UseMemory(ref);
   embed_ptr->version = (short)version;      //Reestablish the original version

   if (the_type & KEPT_AROUND)
      embed_ptr->used_ctr = 1;

   the_type &= EMBED_TYPE_MASK;
   
   embed_ptr->width = pgUnpackNum(&walker);
   embed_ptr->height = pgUnpackNum(&walker);
   embed_ptr->descent = pgUnpackNum(&walker);
   embed_ptr->draw_flags = pgUnpackNum(&walker);
   embed_ptr->modifier = pgUnpackNum(&walker);
   embed_ptr->user_refcon = pgUnpackNum(&walker);
   embed_ptr->data = (void PG_FAR *)pgUnpackNum(&walker);

   embed_ptr->top_extra = (short)pgUnpackNum(&walker);
   embed_ptr->bot_extra = (short)pgUnpackNum(&walker);
   embed_ptr->left_extra = (short)pgUnpackNum(&walker);
   embed_ptr->right_extra = (short)pgUnpackNum(&walker);

#ifdef PG_SUBSETS
   embed_ptr->alignment_flags = pgUnpackNum(&walker);
#endif

   unpack_altsize_text(pg, &walker, embed_ptr->uu.alt_data);

   if (file_version > KEY_REVISION6)
      pgUnpackPtrBytes(&walker, (pg_bits8_ptr)&embed_ptr->border.type);

   if (embed_ptr->data) {
      
      switch (the_type) {

         case embed_control:
            break;
            
         case embed_bitmap:
            unpack_pic_embed(&walker, embed_ptr);
            break;

         case embed_polygon:
            embed_ptr->data = (void PG_FAR *)pgUnpackPoly(&walker);
            break;
         
         case embed_mac_pict:
            unpack_pic_embed(&walker, embed_ptr);
#ifdef MAC_PLATFORM
            embed_ptr->data = NULL;

            if (input_byte_size = pgGetUnpackedSize(&walker)) {
               Handle         the_pict;
               
               if (the_pict = NewHandle(input_byte_size)) {
                  embed_ptr->data = (void *)the_pict;
                  HLock(the_pict);
                  pgUnpackPtrBytes(&walker, (pg_bits8_ptr)*the_pict);
                  HUnlock(the_pict);
               }
            }
#else
            input_byte_size = pgGetUnpackedSize(&walker);
            embed_ptr->data = (void PG_FAR *)MemoryAlloc(mem_globals, 1, input_byte_size, 0);
            pgUnpackBytes(&walker, (memory_ref)embed_ptr->data);
#endif

            break;

            case embed_meta_file:
            unpack_pic_embed(&walker, embed_ptr);
#ifdef WINDOWS_PLATFORM
            {
            if (input_byte_size = pgGetUnpackedSize(&walker)) {
               HANDLE         metabits;
               void PG_FAR    *metaptr;
               
               if (metabits = GlobalAlloc(GMEM_MOVEABLE, input_byte_size)) {
                  
                  metaptr = GlobalLock(metabits);
                  pgUnpackPtrBytes(&walker, (pg_bits8_ptr)metaptr);

#ifndef WIN16_COMPILE
                  embed_ptr->data = (void PG_FAR *)SetMetaFileBitsEx((UINT)input_byte_size, (BYTE PG_FAR *)metaptr);
                  GlobalUnlock(metabits);
                  GlobalFree(metabits);
#else
                  GlobalUnlock(metabits);
                  embed_ptr->data = (void PG_FAR *)SetMetaFileBits(metabits);
#endif
               }
            }
            }
#else
            input_byte_size = pgGetUnpackedSize(&walker);
            embed_ptr->data = (void PG_FAR *)MemoryAlloc(mem_globals, 1, input_byte_size, 0);
            pgUnpackBytes(&walker, (memory_ref)embed_ptr->data);
#endif
            
            break;
            
         case embed_pg_pict:
            unpack_pic_embed(&walker, embed_ptr);
            embed_ptr->data = (void PG_FAR *)pgUnpackPict(&walker);
            break;
         
         case embed_mac_vm_pict:
         case embed_os2_meta_file:
         case embed_dibitmap:
         case embed_wbitmap:
         case embed_gif_pict:
         case embed_jpeg_pict:
            unpack_pic_embed(&walker, embed_ptr);
            input_byte_size = pgGetUnpackedSize(&walker);
            embed_ptr->data = (void PG_FAR *)MemoryAlloc(mem_globals, 1, input_byte_size, 0);
            pgUnpackBytes(&walker, (memory_ref)embed_ptr->data);
            break;
         case embed_ole: // PDA:
            embed_ptr->data = (void*)pgUnpackNum(&walker);
            break;
         case embed_alternate_char:
         case embed_dynamic_string:
         case embed_user_data:
         case embed_bookmark_start:
         case embed_bookmark_end:
            input_byte_size = pgGetUnpackedSize(&walker);
            if (input_byte_size >= ALT_SIZE)
            {
               embed_ptr->data = (void PG_FAR *)MemoryAlloc(mem_globals, 1, input_byte_size, 0);
               pgUnpackBytes(&walker, (memory_ref)embed_ptr->data);
            }
            else
            {
               pgFillBlock((pg_char_ptr)embed_ptr->uu.alt_data, ALT_SIZE * sizeof(pg_char), 0);
               embed_ptr->data = NULL;
               unpack_altsize_text(pg, &walker, embed_ptr->uu.alt_data);
            }
            break;

         case embed_qt_movie:
            unpack_pic_embed(&walker, embed_ptr);
#ifdef MAC_PLATFORM
#endif
            break;

         case embed_qt_flat_movie:
            unpack_pic_embed(&walker, embed_ptr);
            break;

         case embed_time:
           case embed_date:
            embed_ptr->uu.date_info.time = pgUnpackNum(&walker);
            embed_ptr->uu.date_info.time_type = (short)pgUnpackNum(&walker);
            embed_ptr->uu.date_info.format = (short)pgUnpackNum(&walker);
            break;

           case embed_cur_page_num:
           case embed_cur_sect_num:
            break;

         case embed_horiz_rule:
            embed_ptr->uu.line_info.line_size = (short)pgUnpackNum(&walker);
            embed_ptr->uu.line_info.line_shading = (short)pgUnpackNum(&walker);
            embed_ptr->uu.line_info.line_alignment = (short)pgUnpackNum(&walker);
            embed_ptr->uu.line_info.line_width_type = (short)pgUnpackNum(&walker);
            embed_ptr->uu.line_info.line_width = pgUnpackNum(&walker);
            break;
      }
   }

// Now replace all earlier occurrences with this embed_ref in the style_info array:

   num_styles = GetMemorySize(pg->t_formats);
   
   if (!(callback = (embed_callback)pg->globals->embed_callback_proc))
      callback = pgDefaultEmbedCallback;

   custom_data = pgGetUnpackedPtr(&walker, &custom_size);
   callback(pg, embed_ptr, the_type, EMBED_READ_DATA, embed_ptr->user_refcon,
         (long)custom_data, custom_size);

   called_ctr = 0;

   for (styles = UseMemory(pg->t_formats); num_styles; ++styles, --num_styles)
      if (ref_id == (long)styles->embed_object && !(styles->class_bits & EMBED_READ_BIT)) {
      
      ++embed_ptr->used_ctr;
      styles->embed_object = ref;
      styles->class_bits |= EMBED_READ_BIT;
      styles->procs.insert_proc = embed_setup_insert;
   
      if (the_type != embed_alternate_char
         && the_type != embed_dynamic_string)
         styles->procs.char_info = embed_char_info;
      else
         styles->procs.char_info = pg->globals->def_style.procs.char_info;

      styles->procs.init = pgEmbedStyleInit;
      styles->procs.draw = embed_draw;
      styles->procs.measure = measure_embed;
      styles->procs.duplicate = embed_copy;
      styles->procs.delete_style = embed_delete;
      styles->procs.track_ctl = track_embed_ctl;
      styles->embed_entry = (long)callback;

      embed_ptr->style = styles;
      embed_ptr->style_refcon = styles->embed_style_refcon;
      callback(pg, embed_ptr, the_type, EMBED_INIT, embed_ptr->style_refcon, called_ctr, (long)styles);
      embed_ptr->style = NULL;

      ++called_ctr;
   }

   embed_ptr->version = CURRENT_EMBED_VERSION;

   UnuseMemory(pg->t_formats);
   UnuseMemory(walker.data_ref);
   UnuseMemory(ref);

   if (pg->globals->app_init_proc)
      pg->globals->app_init_proc(pg, ref, EMBED_REF_INIT);

   mem_globals->current_id = old_current_id;
   
   return      TRUE;
}


/* pgInitEmbedProcs sets up the I/O handlers for embed_refs. If init_proc is
non-null it will get called for each embed_ref that is successfully read from
the file. The callback parameter becomes the embed_ref user callback function. */

PG_PASCAL (void) pgInitEmbedProcs (pg_globals_ptr globals, embed_callback callback,
      app_init_read init_proc)
{
   pgSetHandler(globals, embedded_item_key, pgEmbedReadHandler, NULL, NULL, NULL);
   globals->embed_callback_proc = (long)callback;
   globals->app_init_proc = init_proc;
}


/* pgSaveAllEmbedRefs saves all embed_refs found in the style_info records. Since
an embed_ref can be "shared" by multiple style runs, each unique embed_ref is
saved only once (which is also why we need a separate function to save these
refs descretely).  */

PG_PASCAL (pg_error) pgSaveAllEmbedRefs (pg_ref pg, file_io_proc io_proc, file_io_proc data_io_proc,
      long PG_FAR *file_position, file_ref filemap)
{
   paige_rec_ptr     pg_rec;
   embed_ref         ref;
   style_info_ptr    styles;
   memory_ref        key_data, custom_ref;
   pack_walk         walker;
   embed_callback    callback;
   short          num_styles, index;
   file_io_proc      data_proc;
   pg_error       result;
   
   pg_rec = UseMemory(pg);
   key_data = MemoryAlloc(pg_rec->globals->mem_globals, 1, 0, 32);
   custom_ref = MemoryAlloc(pg_rec->globals->mem_globals, 1, 0, 32);

   num_styles = (short)GetMemorySize(pg_rec->t_formats);
   styles = UseMemory(pg_rec->t_formats);
   
   result = NO_ERROR;

   if (!(data_proc = data_io_proc))
      data_proc = io_proc;

   for (index = 0; index < num_styles; ++index) {
      
      if (ref = styles[index].embed_object)
         if (!have_saved_ref(styles, index)) {
         
         SetMemorySize(key_data, 0);
         pgSetupPacker(&walker, key_data, 0);
         pack_embed_ref(&walker, ref);
         callback = (embed_callback)styles[index].embed_entry;
         custom_data_callback(pg_rec, &walker, UseMemory(ref), callback, custom_ref);
         UnuseMemory(ref);
         pgFinishPack(&walker);

         result = pgWriteKeyData(pg, embedded_item_key, UseMemory(key_data), walker.transfered,
               0, io_proc, data_proc, file_position, filemap);
         UnuseMemory(key_data);
         
         if (result)
            break;
      }
   }
   
   DisposeMemory(key_data);
   DisposeMemory(custom_ref);
   UnuseMemory(pg_rec->t_formats);
   UnuseMemory(pg);
   
   return      result;
}



/* pgSaveEmbedRef is called by apps that want to save an embed_ref that is
not saved in the normal way. In other words, an embed ref that is marked for
"keep_around" might not exist in the style_info record(s) and thus needs to
be saved descretely. */

PG_PASCAL (pg_error) pgSaveEmbedRef (pg_ref pg, embed_ref ref, long element_info,
      file_io_proc io_proc, file_io_proc data_io_proc, long PG_FAR *file_position,
      file_ref filemap) 
{
   paige_rec_ptr           pg_rec;
   memory_ref              key_data;
   pack_walk               walker;
   file_io_proc            data_proc;
   pg_error             result;

   pg_rec = UseMemory(pg);
   key_data = MemoryAlloc(pg_rec->globals->mem_globals, 1, 0, 32);
   pgSetupPacker(&walker, key_data, 0);
   pack_embed_ref(&walker, ref);
   pgFinishPack(&walker);
   
   if (!(data_proc = data_io_proc))
      data_proc = io_proc;

   result = pgWriteKeyData(pg, embedded_item_key, UseMemory(walker.data_ref), walker.transfered,
         element_info, io_proc, data_proc, file_position, filemap);
   
   UnuseAndDispose(walker.data_ref);
   
   UnuseMemory(pg);

   return      result;
}


/* pgEmbedStyleInit is the style_info function to initialize the associated style
with an embedded ref. */

PG_PASCAL (void) pgEmbedStyleInit (paige_rec_ptr pg, style_info_ptr style, font_info_ptr font)
{
   pg_embed_ptr         embed_ptr;
   long              object_ascent, object_descent;

   if (style->class_bits & EMBED_INITED_BIT)
      return;
   
   pg->globals->def_style.procs.init(pg, style, font);
   
   embed_ptr = UseMemory(style->embed_object);

   if (!(embed_ptr->type & USE_TEXT_HEIGHT)) {
      
      object_ascent = embed_ptr->height - embed_ptr->descent + embed_ptr->top_extra;
      object_descent = embed_ptr->descent + embed_ptr->bot_extra;
   
      style->ascent += (short)(object_ascent - style->ascent);
      style->descent += (short)(object_descent - (style->descent + style->leading));
   }

   UnuseMemory(style->embed_object);
   
   style->class_bits |= EMBED_INITED_BIT;
}


/* pgInitEmbedStyleInfo places all the required function pointers in style & font
and initializes the "mask" fields.  The will_be_inserted param is TRUE if
the data does not yet exist (will be inserted), but FALSE if the data is
already in the text (makes a difference which "setup insert" hook we set).
The function returns the embed_ref type. */

PG_PASCAL (long) pgInitEmbedStyleInfo (paige_rec_ptr pg, long position, embed_ref ref,
   short stylesheet_option, embed_callback callback, long callback_refcon,
   style_info_ptr style, style_info_ptr mask, font_info_ptr font,
   font_info_ptr font_mask, pg_boolean will_be_inserted)
{
   pg_embed_ptr         new_embed;
   embed_callback       use_callback;
   pg_boolean           character_type;
   long              new_type;
   short             use_stylesheet_option;

   if (!(use_callback = callback))
      use_callback = pgDefaultEmbedCallback;
   
   use_stylesheet_option = stylesheet_option;
   
   if (mask && !pg->import_control)
      pgGetStyleInfo(pg->myself, NULL, FALSE, style, mask);

   if (font) {
   
      pgGetFontInfo(pg->myself, NULL, FALSE, font, font_mask);
      
      if (font_mask)
         pgFillBlock(font_mask, sizeof(font_info), -1);
   }
   
   if (mask)
      pgFillBlock(&mask->procs, sizeof(pg_style_hooks), 0);
   
   if (!pg->import_control)
      pgSetSelection(pg->myself, position, position, 0, FALSE);

   new_embed = UseMemory(ref);
   new_type = new_embed->type & EMBED_TYPE_MASK;
   
   if (will_be_inserted)
      style->procs.insert_proc = embed_setup_insert_init;
   else
      style->procs.insert_proc = embed_setup_insert;

   character_type = (pg_boolean)(new_type == embed_alternate_char || new_type == embed_dynamic_string);
   style->procs.char_info = pg->globals->def_style.procs.char_info;

   if ((new_embed->type & EMBED_CONTROL_FLAG) || !character_type) {
   
      style->class_bits |= (STYLE_IS_CONTROL | CANT_TRANS_BIT);
      
      if (!character_type && will_be_inserted)
         style->procs.char_info = embed_char_info;
   }     
   else if (new_embed->type & GROUP_TEXT_FLAG) {
   
      style->class_bits |= (CANNOT_BREAK | GROUP_CHARS_BIT | CANT_TRANS_BIT);
   }
   
   if (new_embed->type & REQUIRES_ACTIVATE_FLAG)
   {
      style->procs.activate_proc = embed_activate;
      style->class_bits |= ACTIVATE_ENABLE_BIT;
   }

   style->procs.init = pgEmbedStyleInit;
   style->procs.draw = embed_draw;
   style->procs.measure = measure_embed;
   style->procs.duplicate = embed_copy;
   style->procs.delete_style = embed_delete;
   style->procs.track_ctl = track_embed_ctl;

   style->embed_entry = (long)use_callback;
   style->embed_object = ref;
   style->embed_refcon = new_embed->user_refcon;
   style->embed_style_refcon = callback_refcon;
   
   style->char_bytes = 1;      // Insertions normally require two bytes

   style->embed_id = pgUniqueID(pg->myself);

   style->styles[super_impose_var] = use_stylesheet_option;

   use_callback(pg, new_embed, new_type, EMBED_VMEASURE, callback_refcon, (long)style, 0);
   UnuseMemory(ref);

   if (mask) {
   
      mask->procs.init = (style_init_proc)SET_MASK_BITS;
      mask->procs.draw = (text_draw_proc)SET_MASK_BITS;
      mask->procs.insert_proc = (setup_insert_proc)SET_MASK_BITS;
      mask->procs.measure = (measure_proc)SET_MASK_BITS;
      mask->procs.duplicate = (dup_style_proc)SET_MASK_BITS;
      mask->procs.delete_style = (delete_style_proc)SET_MASK_BITS;
      mask->procs.track_ctl = (track_control_proc)SET_MASK_BITS;
       mask->procs.char_info = (char_info_proc)SET_MASK_BITS;
   
      mask->embed_entry = SET_MASK_BITS;
      mask->embed_object = (memory_ref)SET_MASK_BITS;
      mask->embed_refcon = SET_MASK_BITS;
      mask->embed_style_refcon = SET_MASK_BITS;
      mask->embed_id = SET_MASK_BITS;
      mask->char_bytes = SET_MASK_BITS;
      mask->class_bits = SET_MASK_BITS;
      mask->styles[super_impose_var] = SET_MASK_BITS;
   }

   return   new_type;
}


/******************************** Local functions ******************************/


/* measure_embed is the style_info function that PAIGE calls to measure char
width. */

STATIC_PASCAL (void) measure_embed (paige_rec_ptr pg, style_walk_ptr walker,
      pg_char_ptr data, long length, pg_short_t slop, long PG_FAR *positions,
      short PG_FAR *types, short measure_verb, long current_offset, short scale_widths,
      short call_order)
{
   register long PG_FAR       *char_locs;
   pg_embed_measure           measure_struct;
   pg_embed_measure_ptr       measure_ptr;
   embed_callback             callback;
   style_info_ptr             style;
   font_info_ptr              font;
   pg_embed_ptr               embed_ptr;
   long                    char_ctr, width_average, the_type;
   long                    ending_width, use_width, incrementing_width;

   if (types)
      pgFillBlock(types, length * sizeof(short), 0);
   
   measure_struct.walker = walker;
   measure_struct.text = data;
   measure_struct.text_size = length;
   measure_struct.slop = slop;
   measure_struct.positions = positions;
   measure_struct.types = types;
   measure_struct.measure_verb = measure_verb;
   measure_struct.current_offset = current_offset;
   measure_struct.call_order = call_order;
   measure_struct.scale_verb = scale_widths;
   measure_ptr = &measure_struct;

   style = walker->cur_style;
   font = walker->cur_font;
   embed_ptr = UseMemory(style->embed_object);
   embed_ptr->style = style;
   embed_ptr->style_refcon = style->embed_style_refcon;
   callback = (embed_callback)style->embed_entry;

   the_type = embed_ptr->type & EMBED_TYPE_MASK;

   if (the_type == embed_alternate_char || the_type == embed_dynamic_string) {
      memory_ref        temp_ref;
      long PG_FAR       *temp_positions;
      pg_char_ptr       the_text;
      short          the_size;

      if (embed_ptr->data) {
         
         the_text = UseMemory((memory_ref)embed_ptr->data);
         the_size = (short)GetMemorySize((memory_ref)embed_ptr->data);
      }
      else {
         
         the_text = (pg_char_ptr)&embed_ptr->uu.alt_data;
         the_size = (short)pgCStrLength((pg_c_string_ptr)the_text);
      }

      if (the_type == embed_dynamic_string) {
         long     max_size;
         
         max_size = embed_ptr->modifier & 0x0000FFFF;
         
         the_size = (short)callback(pg, embed_ptr,
               the_type, EMBED_SWAP, embed_ptr->style_refcon, (long)the_text, max_size);
      }

      if (the_size > length) {

         temp_ref = MemoryAlloc(pg->globals->mem_globals, sizeof(long), the_size + 1, 0);
         temp_positions = UseMemory(temp_ref);
         *temp_positions = *positions;
      }
      else {
         
         temp_ref = MEM_NULL;
         temp_positions = positions;
      }
      
      if (the_size) {
      
         pgMeasureProc(pg, walker, the_text, the_size, slop, temp_positions, NULL,
               measure_verb, current_offset, scale_widths, call_order);

         embed_ptr->width = temp_positions[the_size] - *temp_positions;
         
         if (embed_ptr->type & INCLUDE_EXTRA_SPACES)
            embed_ptr->width += extra_trailing_spaces(pg, walker, data, length);
      }
      else
         embed_ptr->width = 0;

      if (temp_ref)
         UnuseAndDispose(temp_ref);
      if (embed_ptr->data)
         UnuseMemory((memory_ref)embed_ptr->data);
   }

   if (measure_verb != measure_draw_locs)
      callback(pg, embed_ptr, the_type, EMBED_MEASURE, embed_ptr->style_refcon, (long)measure_ptr, 0);

   if (embed_empty(embed_ptr))
      if (embed_ptr->width < embed_ptr->empty_width)
         embed_ptr->width = embed_ptr->empty_width;
   
   use_width = embed_ptr->width;

   if (embed_ptr->minimum_width)
      if (use_width < embed_ptr->minimum_width)
         use_width = embed_ptr->minimum_width;

   char_locs = positions;
   ending_width = use_width + embed_ptr->left_extra + embed_ptr->right_extra;

   width_average = ending_width / length;
   incrementing_width = *char_locs;
   ending_width += incrementing_width;

   for (char_ctr = 0; char_ctr < length; ++char_ctr, incrementing_width += width_average)
      char_locs[char_ctr] = incrementing_width;
   
   char_locs[char_ctr] = ending_width;

   embed_ptr->style = NULL;
   UnuseMemory(style->embed_object);
}

/* embed_activate is the embed bridge for the activate style type */

STATIC_PASCAL (void) embed_activate (paige_rec_ptr pg_rec, style_info_ptr style,
      select_pair_ptr text_range, pg_char_ptr text, short front_back_state,
      short perm_state, pg_boolean show_hilite)
{
   embed_callback             callback;
   pg_embed_ptr               embed_ptr;
   pg_embed_activate          activate_data;

   embed_ptr = UseMemory(style->embed_object);
   embed_ptr->style = style;
   embed_ptr->style_refcon = style->embed_style_refcon;
   callback = (embed_callback)style->embed_entry;
   
   activate_data.style = style;
   activate_data.text_range = text_range;
   activate_data.text = text;
   activate_data.front_back_state = front_back_state;
   activate_data.perm_state = perm_state;
   activate_data.show_hilite = show_hilite;

   callback(pg_rec, embed_ptr, embed_ptr->type & EMBED_TYPE_MASK, (short)((pg_rec->flags & (PERM_DEACT_BIT | DEACT_BIT)) ? EMBED_DEACTIVATE : EMBED_ACTIVATE), embed_ptr->style_refcon, (long)&activate_data, 0);
}


/* track_embed_ctl gets called by PAIGE to track a "control" type item. */

STATIC_PASCAL (long) track_embed_ctl (paige_rec_ptr pg, short verb,
      t_select_ptr first_select, t_select_ptr last_select, style_walk_ptr styles,
      pg_char_ptr associated_text, point_start_ptr bounds_info, short modifiers,
      long track_refcon)
{
   pg_embed_ptr            embed_ptr;
   style_info_ptr          embed_style;
   embed_callback          callback;
   pg_embed_click          click_struct;
   pg_embed_click_ptr         click_ptr;
   long                 track_result;
   t_select_ptr            hilite_select;
   select_pair             style_range;
   short                command;

   embed_style = styles->cur_style;
   
   pgFillBlock(&click_struct, sizeof(pg_embed_click), 0);

   click_struct.first_select = first_select;
   click_struct.last_select = last_select;
   click_struct.modifiers = modifiers;
   click_ptr = &click_struct;

   embed_ptr = UseMemory(embed_style->embed_object);
   style_range.begin = styles->prev_style_run->offset;
   style_range.end = styles->next_style_run->offset;

   if (style_range.end > pg->t_length)
      style_range.end = pg->t_length;
   
   get_visual_frame(pg, embed_ptr, &style_range, &click_struct.bounds);
   UnuseMemory(embed_style->embed_object);

   if (verb == mouse_down) {
      
      click_struct.point = first_select->original_pt;
      pgSubPt(&pg->scroll_pos, &click_struct.point);
      pgAddPt(&pg->port.origin, &click_struct.point);

      if (modifiers & (~WORD_MOD_BIT))
         return   0L;

      if (!pgPtInRect(&click_struct.point, &click_struct.bounds))
         return   0L;
   }
   else {
   
      click_struct.point = last_select->original_pt;
      pgSubPt(&pg->scroll_pos, &click_struct.point);
      pgAddPt(&pg->port.origin, &click_struct.point);
   }
   
   embed_ptr = UseMemory(embed_style->embed_object);
   embed_ptr->style = embed_style;
   embed_ptr->style_refcon = embed_style->embed_style_refcon;
   callback = (embed_callback)embed_style->embed_entry;

   if (modifiers & WORD_MOD_BIT)
      command = EMBED_DOUBLECLICK;
   else
   if (verb == mouse_down)
      command = EMBED_MOUSEDOWN;
   else
   if (verb == mouse_moved)
      command = EMBED_MOUSEMOVE;
   else
      command = EMBED_MOUSEUP;
   
   track_result = callback(pg, embed_ptr,
         embed_ptr->type & EMBED_TYPE_MASK, command, embed_style->embed_style_refcon,
         (long)click_ptr, 0);

   embed_ptr->style = NULL;
   UnuseMemory(embed_style->embed_object);
   
   hilite_select = UseMemory(pg->select);

   if (track_result) {
   
      hilite_select->flags |= SELECT_ON_CTL;
      hilite_select[1].flags |= SELECT_ON_CTL;
   }
   else {
   
      hilite_select->flags &= (~SELECT_ON_CTL);
      hilite_select[1].flags &= (~SELECT_ON_CTL);
   }
   
   UnuseMemory(pg->select);

   return   track_result;
}



/* embed_draw gets called by PAIGE to draw text. In this case we draw our
embedded object. */

STATIC_PASCAL (void) embed_draw (paige_rec_ptr pg, style_walk_ptr walker, pg_char_ptr data,
      pg_short_t offset, pg_short_t length, draw_points_ptr draw_position,
      long extra, short draw_mode)
{
   style_info_ptr             style;
   font_info_ptr              font;
   rectangle                  draw_frame;
   rectangle_ptr              draw_frame_ptr;
   embed_callback             callback;
   long                    the_type, scaled_width, scaled_height, scaled_descent;
   pg_embed_ptr               embed_ptr;

   style = walker->cur_style;
   font = walker->cur_font;   
   embed_ptr = UseMemory(style->embed_object);
   
   the_type = embed_ptr->type & EMBED_TYPE_MASK;
   callback = (embed_callback)style->embed_entry;

   embed_ptr->style = style;
   embed_ptr->style_refcon = style->embed_style_refcon;

   scaled_width = embed_ptr->width;
   scaled_height = embed_ptr->height;
   scaled_descent = embed_ptr->descent;

   if (pg->scale_factor.scale) {
   
      pgScaleLong(pg->scale_factor.scale, 0, &scaled_width);
      pgScaleLong(pg->scale_factor.scale, 0, &scaled_height);
      pgScaleLong(pg->scale_factor.scale, 0, &scaled_descent);
   }

   draw_frame.top_left = draw_position->from;
   draw_frame.top_left.h += embed_ptr->left_extra;
   draw_frame.bot_right.h = draw_frame.top_left.h + scaled_width;
   draw_frame_ptr = &draw_frame;

   if (embed_ptr->type & USE_TEXT_HEIGHT) {
      
      draw_frame.top_left.v -= draw_position->ascent;
      draw_frame.bot_right.v = draw_position->from.v + draw_position->descent;
   }
   else {
   
      draw_frame.top_left.v -= (scaled_height - scaled_descent);
      draw_frame.bot_right.v = draw_frame.top_left.v + scaled_height;
   }

   if (the_type == embed_alternate_char || the_type == embed_dynamic_string) {
      pg_char_ptr       the_chars;
      short          the_size;
      
      if (embed_ptr->data) {
         
         the_chars = UseMemory((memory_ref)embed_ptr->data);
         the_size = (short)GetMemorySize((memory_ref)embed_ptr->data);
      }
      else {
         the_chars = (pg_char_ptr)&embed_ptr->uu.alt_data;
         the_size = (short)pgCStrLength((pg_c_string_ptr)the_chars);
      }

      if (the_type == embed_dynamic_string) {
         long     max_size;
         
         max_size = embed_ptr->modifier & 0x0000FFFF;
         the_size = (short)callback(pg, embed_ptr, embed_dynamic_string,
               EMBED_SWAP, embed_ptr->style_refcon, (long)the_chars, max_size);
      }

      if (the_size)
         pgDrawProc(pg, walker, the_chars, 0, the_size, draw_position, extra, draw_mode);

      if (embed_ptr->data)
         UnuseMemory((memory_ref)embed_ptr->data);
   }
   else
      callback(pg, embed_ptr, the_type, EMBED_DRAW,
            embed_ptr->style_refcon, (long)draw_frame_ptr, (long)draw_position);

   embed_ptr->style = NULL;
   UnuseMemory(style->embed_object);
}


/* embed_copy gets called by PAIGE to duplicate any memory allocations within
the style given. In our case, we usually have a memory_ref sitting in the style. */

STATIC_PASCAL (void) embed_copy (paige_rec_ptr src_pg, paige_rec_ptr target_pg,
      short reason_verb, format_ref all_styles, style_info_ptr style)
{
   paige_rec_ptr           pg_for_id;
   pg_embed_ptr            embed_ptr;
   pg_ref                  source_of_id;

   if (reason_verb == new_stylesheet_reason)
      return;

   embed_ptr = UseMemory(style->embed_object);
   source_of_id = MEM_NULL;

   if (!(pg_for_id = target_pg))
      pg_for_id = src_pg;
   
   if (pg_for_id) {
      
      source_of_id = pg_for_id->myself;
      pg_for_id = pgUseSharedPg(&source_of_id);
      ++pg_for_id->next_id;
      style->embed_id = pg_for_id->next_id;
   }

   if ((embed_ptr->type & NOT_SHARED_FLAG) && ( (reason_verb == copy_reason) || (reason_verb == paste_reason))) {
      embed_callback    callback;
      memory_ref        the_copy;

      the_copy = MemoryDuplicate(style->embed_object);
      UnuseMemory(style->embed_object);
      style->embed_object = the_copy;
      embed_ptr = UseMemory(style->embed_object);
      embed_ptr->used_ctr = 0;
      callback = (embed_callback) style->embed_entry;
      callback(pg_for_id, embed_ptr, embed_ptr->type & EMBED_TYPE_MASK, EMBED_COPY,
               style->embed_style_refcon, reason_verb, 0);
   }

   ++embed_ptr->used_ctr;

   UnuseMemory(style->embed_object);
   
   if (source_of_id)
      UnuseMemory(source_of_id);
}


/* embed_delete gets called by PAIGE when a style_info record is about to be
deleted. */

STATIC_PASCAL (void) embed_delete (paige_rec_ptr pg, pg_globals_ptr globals,
      short reason_verb, format_ref all_styles, style_info_ptr style)
{
   pg_embed_ptr            embed_ptr;
   embed_callback          callback;
   
   embed_ptr = UseMemory(style->embed_object);
   
   if (embed_ptr->used_ctr)
      embed_ptr->used_ctr -= 1;

   if (!embed_ptr->used_ctr) {
      
      callback = (embed_callback)style->embed_entry;
      embed_ptr->style = style;
      callback(pg, embed_ptr, embed_ptr->type & EMBED_TYPE_MASK, EMBED_DESTROY,
               style->embed_style_refcon, reason_verb, 0);
      embed_ptr->style = NULL;
      UnuseAndDispose(style->embed_object);
   }
   else
      UnuseMemory(style->embed_object);
}



/* embed_setup_insert_init is the setup_insert proc for the FIRST INSERT,
which causes my own data to get inserted correctly. Then, within this function,
we setup for the "real" setup_insert proc before returning. */

STATIC_PASCAL (pg_boolean) embed_setup_insert_init (paige_rec_ptr pg, style_info_ptr style,
      long position)
{
   style->procs.insert_proc = embed_setup_insert;     // This gets called next time.
   return   TRUE;
}


/* embed_setup_insert gets called when one or more chars are about to be inserted,
in which case we do NOT want our embedded object to extend to additional chars. */

STATIC_PASCAL (pg_boolean) embed_setup_insert (paige_rec_ptr pg, style_info_ptr style,
      long position)
{
   style->embed_object = MEM_NULL;
   style->embed_refcon = 0;
   style->embed_id = 0;
   style->class_bits = 0;
   style->char_bytes = 0;
   style->styles[super_impose_var] = 0;
   style->procs = pg->globals->def_style.procs;

   return   TRUE;
}


/* embed_char_info returns info about word breaks (because we want to treat an
item as a "word". Note that we don't get called (hook was not set) for
character substitution.  */

STATIC_PASCAL (long) embed_char_info (paige_rec_ptr pg, style_walk_ptr style_walker,
      pg_char_ptr data, long block_offset, long offset_begin, long offset_end, long char_offset, long mask_bits)
{
   long        result;
   
   result = WORD_BREAK_BIT | WORD_SEL_BIT;
   
   if (data[char_offset] == DUMMY_RIGHT_EMBED)
      result |= (INCLUDE_BREAK_BIT | INCLUDE_SEL_BIT | LAST_HALF_BIT);
   else
      result |= FIRST_HALF_BIT;

   return   (result & mask_bits);
}


/* next_style_same_ref returns TRUE if the next style is an identical embed_ref. */

static pg_boolean next_style_same_ref (paige_rec_ptr pg, style_walk_ptr walker)
{
   pg_short_t        style_index;
   
   if (walker->next_style_run->offset >= pg->t_length)
      return   FALSE;
   
   style_index = walker->next_style_run->style_item;
   
   return   (walker->cur_style->embed_object == walker->style_base[style_index].embed_object);
}


/* previous_style_same_ref returns TRUE if the previous style has an identical
embed_ref. */

static pg_boolean previous_style_same_ref (paige_rec_ptr pg, style_walk_ptr walker)
{
   pg_short_t        style_index;
   
   if (!walker->prev_style_run->offset)
      return   FALSE;
   
   --walker->prev_style_run;
   style_index = walker->prev_style_run->style_item;
   ++walker->prev_style_run;

   return   (walker->cur_style->embed_object == walker->style_base[style_index].embed_object);
}


/* embed_empty returns TRUE if the embed data is knowingly empty. */

static pg_boolean embed_empty (pg_embed_ptr embed_ptr)
{
   long              length_test;

   switch (embed_ptr->type & EMBED_TYPE_MASK) {

      case embed_user_data:
         length_test = embed_ptr->modifier;
         break;

      case embed_bookmark_start:
      case embed_bookmark_end:
      case embed_alternate_char:
      case embed_dynamic_string:
         if (embed_ptr->data)
            length_test = GetMemorySize((memory_ref)embed_ptr->data);
         else
            length_test = pgCStrLength((pg_c_string_ptr)embed_ptr->uu.alt_data);

         break;
      
      default:
         length_test = 1;
         break;
   }
   
   return   (length_test == 0);
}



/* standard_embed_draw is the default drawing proc. */

static void standard_embed_draw (paige_rec_ptr pg, pg_embed_ptr item,
      rectangle_ptr draw_bounds, draw_points_ptr draw_position, pg_boolean drawing_to_bitmap)
{
   memory_ref        	the_data;
   graf_device_ptr      target_device;
   long           		low_modifier, hi_modifier;

   if (drawing_to_bitmap)
      target_device = &pg->globals->offscreen_port;
   else
      target_device = &pg->port;

   low_modifier = item->modifier & 0x0000FFFF;
   hi_modifier = item->modifier >> 16;

   the_data = (memory_ref)item->data;

   switch (item->type & EMBED_TYPE_MASK) {

      case embed_oval:
         pgFrameObject(target_device, draw_bounds, 0, object_oval, (short)low_modifier);
         break;

      case embed_roundrectangle:
         pgFrameObject(target_device, draw_bounds, (short)hi_modifier, object_roundrect, (short)low_modifier);
         break;

      case embed_rectangle:
      case embed_user_box:
         pgFrameObject(target_device, draw_bounds, 0, object_rect, (short)low_modifier);
         break;

      case embed_bitmap:
         pgCopyBits(target_device, (pg_bitmap_ptr)UseMemory(the_data),
               NULL, draw_bounds, bits_copy);
         UnuseMemory(the_data);
         break;
         
      case embed_polygon:
         pgFramePoly(target_device, the_data, draw_bounds, (short)low_modifier);
         break;

      case embed_control:
#ifdef MAC_PLATFORM
      {
         ControlHandle     ctl;
         
         ctl = (ControlHandle)the_data;
         //Code change by Timothy Paustian Monday, May 8, 2000 8:57:10 PM
		//Changed to Opaque call for Carbon
		//This is not used by PPC Carbon
        #if ACCESSOR_CALLS_ARE_FUNCTIONS == 1
			GetControlBounds(ctl, &draw_bounds);
			EmbedControl(ctl, target_device->machine_var);
		#else
			SetRect(&(**ctl).contrlRect, draw_bounds->top_left.h,
               draw_bounds->top_left.v, draw_bounds->bot_right.h,
               draw_bounds->bot_right.v);
            (**ctl).contrlOwner = (WindowPtr)target_device->machine_var;
        #endif
         Draw1Control(ctl);
      }
#else
      {
         pg_char_ptr unknown_text;
         co_ordinate centered_pt;
         short    length;
         
         pgPaintObject(target_device, draw_bounds, 0, object_rect);
         unknown_text = (pg_char_ptr)MAC_CONTROL_STR;
         length = (short)pgCStrLength((pg_c_string_ptr)unknown_text);
         pgTextOut (target_device, center_unknown_text(item, target_device, unknown_text, length, draw_bounds, &centered_pt), unknown_text, length, 0);
      }
#endif
         break;

      case embed_ole: // PDA:
      {
         pg_char_ptr unknown_text;
         co_ordinate centered_pt;
         short    length;
         
         pgFrameObject(target_device, draw_bounds, 0, object_rect, 1);
         unknown_text = (pg_char_ptr)OLE_STR;
         length = (short)pgCStrLength((pg_c_string_ptr)unknown_text);
         pgTextOut (target_device, center_unknown_text(item, target_device, unknown_text, length, draw_bounds, &centered_pt), unknown_text, length, 0);
      }
         break;
      
      case embed_mac_pict:
#ifdef MAC_PLATFORM
      {
         PicHandle         pic;
         Rect           frame;
         
         pic = (PicHandle)the_data;
         SetRect(&frame, draw_bounds->top_left.h,
               draw_bounds->top_left.v, draw_bounds->bot_right.h,
               draw_bounds->bot_right.v);
         EraseRect(&frame);
         DrawPicture(pic, &frame);
      }
#else
      {
         pg_char_ptr unknown_text;
         co_ordinate centered_pt;
         short    length;
         
         pgFrameObject(target_device, draw_bounds, 0, object_rect, 1);
         unknown_text = (pg_char_ptr)MAC_PICTURE_STR;
         length = (short)pgCStrLength((pg_c_string_ptr)unknown_text);
         pgTextOut (target_device, center_unknown_text(item, target_device, unknown_text, length, draw_bounds, &centered_pt), unknown_text, length, 0);
      }
#endif
      break;
      
      case embed_mac_vm_pict:
#ifdef MAC_PLATFORM
      {
         PicHandle         pic;
         Rect           frame;
         pgm_globals_ptr      mem_globals;
         
         mem_globals = GetGlobalsFromRef(the_data);
         SetRect(&frame, draw_bounds->top_left.h,
               draw_bounds->top_left.v, draw_bounds->bot_right.h,
               draw_bounds->bot_right.v);
         EraseRect(&frame);
         pic = (PicHandle)MemoryToHandle(the_data);
         DrawPicture(pic, &frame);
         item->data = (void PG_FAR *) HandleToMemory (mem_globals, (Handle) pic, 1);
      }
#else
      {
         pg_char_ptr unknown_text;
         co_ordinate centered_pt;
         short    length;

         pgFrameObject(target_device, draw_bounds, 0, object_rect, 1);
         unknown_text = (pg_char_ptr)MAC_PICTURE_STR;
         length = (short)pgCStrLength((pg_c_string_ptr)unknown_text);
         pgTextOut (target_device, center_unknown_text(item, target_device, unknown_text, length, draw_bounds, &centered_pt), unknown_text, length, 0);
      }
#endif
      break;
      
      case embed_meta_file:
#ifdef WINDOWS_PLATFORM
        {
        	RECT		meta_rect;
        	
        	RectangleToRect(draw_bounds, NULL, &meta_rect);
        	draw_meta_file(pg, (HDC)target_device->machine_ref, item, &meta_rect);
        }
#else
      {
         pg_char_ptr unknown_text;
         co_ordinate centered_pt;
         short    length;
         
         pgFrameObject(target_device, draw_bounds, 0, object_rect, 1);
         unknown_text = (pg_char_ptr)WIN_META_STR;
         length = pgCStrLength((pg_c_string_ptr)unknown_text);
         pgTextOut (target_device, center_unknown_text(item, target_device, unknown_text, length, draw_bounds, &centered_pt), unknown_text, length, 0);
      }
#endif
         break;

      case embed_os2_meta_file:
#ifndef OS2_PLATFORM
      {
         pg_char_ptr unknown_text;
         co_ordinate centered_pt;
         short    length;
         
         pgFrameObject(target_device, draw_bounds, 0, object_rect, 1);
         unknown_text = (pg_char_ptr)OS2_META_STR;
         length = (short)pgCStrLength((pg_c_string_ptr)unknown_text);
         pgTextOut (target_device, center_unknown_text(item, target_device, unknown_text, length, draw_bounds, &centered_pt), unknown_text, length, 0);
      }
#else
#endif
         break;

      case embed_dibitmap:
#ifndef WINDOWS_PLATFORM
      {
         pg_char_ptr unknown_text;
         co_ordinate centered_pt;
         short    length;
         
         pgPaintObject(target_device, draw_bounds, 0, object_rect);
         unknown_text = (pg_char_ptr)WIN_BMP_STR;
         length = pgCStrLength((pg_c_string_ptr)unknown_text);
         pgTextOut (target_device, center_unknown_text(item, target_device, unknown_text, length, draw_bounds, &centered_pt), unknown_text, length, 0);
      }
#else
#endif
         break;

      case embed_wbitmap:
#ifndef WINDOWS_PLATFORM
      {
         pg_char_ptr unknown_text;
         co_ordinate centered_pt;
         short    length;
         
         pgPaintObject(target_device, draw_bounds, 0, object_rect);
         unknown_text = (pg_char_ptr)WIN_DDBMP_STR;
         length = pgCStrLength((pg_c_string_ptr)unknown_text);
         pgTextOut (target_device, center_unknown_text(item, target_device, unknown_text, length, draw_bounds, &centered_pt), unknown_text, length, 0);
      }
#else
#endif
         break;

      case embed_pg_pict:
         pgDrawPicture(target_device, the_data, draw_bounds, TRUE, TRUE, FALSE);
         break;

      case embed_pg_link:
         break;

      case embed_horiz_rule:
         break;
      
      case embed_user_data:
         break;

      default:
      {
         pg_char_ptr unknown_text;
         co_ordinate centered_pt;
         short    length;
         
         pgPaintObject(target_device, draw_bounds, 0, object_rect);
         unknown_text = (pg_char_ptr)UNKNOWN_STR;
         length = (short)pgCStrLength((pg_c_string_ptr)unknown_text);
         pgTextOut (target_device, center_unknown_text(item, target_device, unknown_text, length, draw_bounds, &centered_pt), unknown_text, length, 0);
      }
         break;
   }
   pgDrawEmbedBorders(pg->globals, target_device, item, draw_bounds);
}

/* standard_embed_dispose is the standard function to dispose an
embedded reference. */

static void standard_embed_dispose (pg_embed_ptr item, long the_type)
{
   memory_ref        the_data;
   
   the_data = (memory_ref)item->data;

   switch (the_type) {

      case embed_control:
#ifdef MAC_PLATFORM
         DisposeControl((ControlRef)the_data);
         break;
#endif

      case embed_mac_pict:
#ifdef MAC_PLATFORM
         DisposeHandle((Handle)the_data);
#else
         DisposeNonNilFailedMemory(the_data);
#endif
         break;
        
        case embed_meta_file:
#ifdef WINDOWS_PLATFORM
            
            DeleteMetaFile((HANDLE)the_data);
#else
         DisposeNonNilFailedMemory(the_data);
#endif
         break;
         
      case embed_bitmap:
         if (the_data)
         {
            pg_bitmap_ptr        bitmap_ptr;

            bitmap_ptr = UseMemory(the_data);
            
            if (bitmap_ptr->palette)
               DisposeMemory(bitmap_ptr->palette);
   
            UnuseAndDispose(the_data);
         }
         break;
      
      case embed_ole: // PDA:
      case embed_polygon:
      case embed_alternate_char:
      case embed_dynamic_string:
      case embed_user_data:
      case embed_mac_vm_pict:
      case embed_os2_meta_file:
      case embed_dibitmap:
      case embed_wbitmap:
      case embed_pg_pict:
      case embed_gif_pict:
      case embed_jpeg_pict:
      default:
         DisposeNonNilFailedMemory(the_data);
            break;
       
      case embed_bookmark_start:
      case embed_bookmark_end:
            break;

      case embed_qt_movie:
      case embed_qt_flat_movie:
      //¥¥
            break;

      case embed_time:
        case embed_date:
        case embed_cur_page_num:
        case embed_cur_sect_num:
            break;
   }
}


/* standard_embed_click is the default click function. We only call this,
however, if the click was truly within the item AND for mouse-down only. */

static long standard_embed_click (paige_rec_ptr pg, pg_embed_ptr item,
      long item_type, pg_embed_click_ptr click_info)
{
   style_info_ptr    embed_style;
   t_select_ptr      selection;

   embed_style = item->style;
   selection = click_info->first_select;

   switch (item_type) {
   
      case embed_rectangle:
      case embed_oval:
      case embed_roundrectangle:
      case embed_arc:
      case embed_bitmap:
      case embed_polygon:
      case embed_pg_link:
      case embed_mac_pict:
      case embed_mac_vm_pict:
        case embed_meta_file:
      case embed_os2_meta_file:
      case embed_dibitmap:
      case embed_wbitmap:
      case embed_pg_pict:
      case embed_gif_pict:
      case embed_jpeg_pict:
      case embed_qt_movie:
      case embed_qt_flat_movie:
      case embed_alternate_char:
      case embed_dynamic_string:
        case embed_time:
        case embed_date:
        case embed_cur_page_num:
        case embed_cur_sect_num:
         pgSetSelection(pg->myself, selection->control_offset,
               selection->control_offset + 2, 0, TRUE);

         return   embed_style->embed_id;
         
      case embed_bookmark_start:
      case embed_bookmark_end:
//       pgSetSelection(pg->myself, selection->control_offset,
//             selection->control_offset + EMBED_DEFAULT_SIZE, 0, TRUE);

         return   embed_style->embed_id;
         
      case embed_control:
         return   embed_style->embed_id;
   }

   return      0;
}


/* initialize_embed_data gets called when a new embedded object is created and
the given data is non-NULL. If vert_pos is non-zero, the descent value of the item is
computed as follows: if negative, the absolute value is determined and that many pixels
is used for the descent. If positive, the value is considered a percentage of the total height
(for example, if vert_pos is 0x000A0000 then the descent is height * 0.10). */

static void initialize_embed_data (pgm_globals_ptr mem_globals, embed_ref the_ref,
      void PG_FAR *data, pg_fixed vert_pos)
{
   rectangle         rect;
   memory_ref        data_ref;
   pg_embed_ptr      embed_ptr;
   pg_char_ptr       alt_char_ptr;
   pg_bitmap_ptr     bitmap_ptr;
   pg_poly_ptr       poly_ptr;
   long           	 data_size, the_type;
   
   embed_ptr = UseMemory(the_ref);
   the_type = embed_ptr->type & EMBED_TYPE_MASK;
   if (the_type == embed_alternate_char || the_type == embed_dynamic_string)
      embed_ptr->type |= USE_TEXT_HEIGHT;
   
   if (data) {

      pgFillBlock(&rect, sizeof(rectangle), 0);
   
      switch (the_type) {
   
         case embed_oval:
         case embed_roundrectangle:
         case embed_rectangle:
         case embed_user_box:
            pgBlockMove(data, &rect, sizeof(rectangle));
            pgBlockMove(&rect, embed_ptr->uu.alt_data, sizeof(rectangle));
            
            if ((embed_ptr->type & EMBED_TYPE_MASK) == embed_roundrectangle)
               if (!embed_ptr->modifier)
                  embed_ptr->modifier = 16;
   
            break;
   
         case embed_control:
#ifdef MAC_PLATFORM
         {
            ControlHandle        ctl;
   
            embed_ptr->data = data;
            ctl = (ControlHandle)data;
            detach_control_from_window(ctl);
            //Code change by Timothy Paustian Monday, May 8, 2000 9:03:54 PM
			//Changed to Opaque call for Carbon
           #if ACCESSOR_CALLS_ARE_FUNCTIONS == 1
			GetControlBounds(ctl, &rect);
			//this is not called by Frontier.
			#else
            RectToRectangle(&(**ctl).contrlRect, &rect);
            #endif
         }
#endif

            break;
         
         case embed_ole: // PDA:
            break;
         case embed_user_data:
            if (embed_ptr->modifier) {
   
               embed_ptr->data = (void PG_FAR *)MemoryAlloc(mem_globals, 1, embed_ptr->modifier, 0);
               pgBlockMove(data, UseMemory((memory_ref)embed_ptr->data), embed_ptr->modifier);
               UnuseMemory((memory_ref)embed_ptr->data);
            }
            
            break;
   
         case embed_bookmark_start:
         case embed_bookmark_end:
         case embed_alternate_char:
         case embed_dynamic_string:
            alt_char_ptr = (pg_char_ptr)data;
            data_size = pgCStrLength((pg_c_string_ptr)alt_char_ptr);
            set_string_data(mem_globals, embed_ptr, the_type, alt_char_ptr, (short)data_size);
            break;
   
         case embed_bitmap:
            bitmap_ptr = (pg_bitmap_ptr)data;
            rect.bot_right.h = bitmap_ptr->width;
            rect.bot_right.v = bitmap_ptr->height;
   
            data_size = (bitmap_ptr->row_bytes * bitmap_ptr->height)
                  + (sizeof(pg_bitmap_rec) - 2);
   
            data_ref = MemoryAlloc(mem_globals, 1, data_size, 0);
            pgBlockMove(data, UseMemory(data_ref), data_size);
            UnuseMemory(data_ref);
            
            embed_ptr->data = (void PG_FAR *)data_ref;
            break;
            
         case embed_polygon:
            poly_ptr = UseMemory((memory_ref)data);
            rect.bot_right.h = poly_ptr->width;
            rect.bot_right.v = poly_ptr->height;
            UnuseMemory((memory_ref)data);
   
            embed_ptr->data = data;
            break;

         case embed_mac_pict:
#ifdef MAC_PLATFORM
         {
            PicHandle         pict;
   
            embed_ptr->data = data;
            pict = (PicHandle)data;
            RectToRectangle(&(**pict).picFrame, &rect);
         }
#else
         {
            embed_ptr->data = data;
            RectToRectangle(&((Mac_PicPtr)UseMemory((memory_ref)data))->picFrame, &rect);
            UnuseMemory((memory_ref)data);
         }
#endif
            break;
      
         case embed_mac_vm_pict:
#ifdef MAC_PLATFORM
         {
            embed_ptr->data = data;
            RectToRectangle(&((PicPtr)UseMemory((memory_ref)data))->picFrame, &rect);
            UnuseMemory((memory_ref)data);
         }
#else
         {
            embed_ptr->data = data;
            RectToRectangle(&((Mac_PicPtr)UseMemory((memory_ref)data))->picFrame, &rect);
            UnuseMemory((memory_ref)data);
         }
#endif
            break;

         case embed_meta_file:
           {
               metafile_ptr         metafile;

               metafile = (metafile_ptr)data;
               embed_ptr->data = (void PG_FAR *)metafile->metafile;
               rect = metafile->bounds;
               embed_ptr->modifier = rect.top_left.h << 16;
               embed_ptr->modifier |= rect.top_left.v;
               embed_ptr->uu.pict_data.mapping_mode = metafile->mapping_mode;
			   embed_ptr->uu.pict_data.meta_ext_x = metafile->x_ext;
			   embed_ptr->uu.pict_data.meta_ext_y = metafile->y_ext;
			   
			   if (pgEmptyRect(&rect)) {
			   		
			   		rect.bot_right.v =  DEF_EMBED_HEIGHT;
			   		rect.bot_right.h = DEF_EMBED_WIDTH;
			   }
           }

           break;

         case embed_pg_pict:
         {
            picture_header_ptr pict_ptr;
            
            pict_ptr = UseMemory((memory_ref)data);
            rect.bot_right.h = pict_ptr->width;
            rect.bot_right.v = pict_ptr->height;
            UnuseMemory((memory_ref)data);
   
            embed_ptr->data = data;
         }
            break;
         
         case embed_os2_meta_file:
            break;
   
         case embed_dibitmap:
            break;
   
         case embed_wbitmap:
            break;
   
         case embed_gif_pict:
#ifdef USING_GIF
            embed_ptr->data = data;
            pgGetGIFRect((memory_ref)data, &rect);
#endif
            break;

         case embed_jpeg_pict:
            break;
         
         case embed_qt_movie:
#ifdef MAC_PLATFORM
#endif
#ifdef WINDOWS_PLATFORM
#endif
#ifdef UNIX_PLATFORM
#endif
#ifdef OS2_PLATFORM
#endif
            break;

         case embed_qt_flat_movie:
            break;

         case embed_time:
           case embed_date:
           case embed_cur_page_num:
           case embed_cur_sect_num:
            embed_ptr->type |= USE_TEXT_HEIGHT;
            embed_ptr->data = data;
            break;
   
         case embed_pg_link:
            break;
   
         case embed_useritem:
            embed_ptr->data = data;
            break;
   
      }
   
      if (!pgEmptyRect(&rect)) {
      
         pgOffsetRect(&rect, -rect.top_left.h, -rect.top_left.v);
         embed_ptr->width = rect.bot_right.h;
         embed_ptr->height = rect.bot_right.v;
         
         if ((long)vert_pos < 0)
            embed_ptr->descent = pgHiWord(pgRoundFixed(-vert_pos));
         else {
            pg_fixed    percent;
            
            percent = pgDivideFixed(vert_pos, 0x00640000);
            embed_ptr->descent = pgHiWord(pgRoundFixed(pgMultiplyFixed(embed_ptr->height << 16, percent)));
         }
         
         embed_ptr->left_extra = embed_ptr->right_extra = 1;
         embed_ptr->bot_extra = embed_ptr->top_extra = 1;
      }
   }
   
   UnuseMemory(the_ref);
}



/* set_string_data places the data for string type(s) into the embed ref. */

static void set_string_data (pgm_globals_ptr mem_globals, pg_embed_ptr embed_ptr, long the_type,
      pg_char_ptr alt_char_ptr, short str_size)
{
   short       data_size, max_size;

   data_size = max_size = (str_size + 1);
   
   if (the_type == embed_dynamic_string)
      max_size = (pgLoWord(embed_ptr->modifier)) + 1;

   if (max_size < ALT_SIZE) {

      if (embed_ptr->data)
      {
         DisposeMemory((memory_ref)embed_ptr->data);
         embed_ptr->data = MEM_NULL;
      }

      if (alt_char_ptr) {
      
         pgFillBlock(&embed_ptr->uu.alt_data, sizeof(pg_char), 0);
         pgBlockMove(alt_char_ptr, &embed_ptr->uu.alt_data, data_size * sizeof(pg_char));
      }
   }
   else {
      memory_ref        data_ref;

      data_ref = MemoryAllocClear(mem_globals, 1, max_size, 0);
      
      if (alt_char_ptr) {
      
         pgBlockMove(alt_char_ptr, UseMemory(data_ref), data_size);
         UnuseMemory(data_ref);
      }

      embed_ptr->data = (void PG_FAR *)data_ref;
   }
}


/* get_visual_frame returns the physical bounds of embed_ptr -- same frame that
is drawn on screen. */

static void get_visual_frame (paige_rec_ptr pg, pg_embed_ptr embed_ptr,
      select_pair_ptr range, rectangle_ptr bounds)
{
   text_block_ptr block;
   point_start_ptr   starts;
   co_ordinate    scroll_pos;
   t_select    associated_pt;
   short       baseline;

   associated_pt.offset = range->begin;
   associated_pt.flags = 0;
   pgCalcSelect(pg, &associated_pt);
   block = pgFindTextBlock(pg, associated_pt.offset, NULL, FALSE, FALSE);
   starts = UseMemoryRecord(block->lines, (long)associated_pt.line, 0, TRUE);
   *bounds = starts->bounds;
   baseline = starts->baseline;
   UnuseMemory(block->lines);
   UnuseMemory(pg->t_blocks);
   
   scroll_pos = pg->scroll_pos;
   pgNegatePt(&scroll_pos);
   pgAddPt(&pg->port.origin, &scroll_pos);
   pgOffsetRect(bounds, scroll_pos.h, scroll_pos.v);

   if (!(embed_ptr->type & USE_TEXT_HEIGHT)) {
   
      bounds->bot_right.v -= baseline;
      bounds->bot_right.v += embed_ptr->descent;
      bounds->top_left.v = bounds->bot_right.v - embed_ptr->height;
   }

   bounds->top_left.h += embed_ptr->left_extra;
   bounds->top_left.v += embed_ptr->top_extra;
   bounds->bot_right.h -= embed_ptr->right_extra;
   bounds->bot_right.v -= embed_ptr->bot_extra;
   
   if (pg->scale_factor.scale) {
      
      scroll_pos = pg->scroll_pos;
      pgNegatePt(&scroll_pos);
      pgScaleRect(&pg->scale_factor, &scroll_pos, bounds);
   }
}




/* The following function returns the total width of all trailing spaces at
the end of the text. */

static long extra_trailing_spaces (paige_rec_ptr pg, style_walk_ptr walker,
      pg_char_ptr text, long length)
{
   long        index, actual_charbytes;
   
   index = length;
   actual_charbytes = walker->cur_style->char_bytes;
   walker->cur_style->char_bytes = 0;

   while (index) {
      
      // Note, I call standard proc because caller wants "real" text measured

      --index;

      if (!pgCharInfoProc(pg, walker, text, NO_BLOCK_OFFSET, 0, length, index, BLANK_BIT)) {
         
         ++index;
         break;
      }
   }
   
   walker->cur_style->char_bytes = (short)actual_charbytes;

   if (index < length)
      return (long)pgTextWidth(&pg->port, &text[index], (short)(length - index));
   
   return   0;
}


/* have_saved_ref if the same exact embed_ref exists before stylebase[index].
This function does not get called if there is no embed_ref in stylebase[index]. */

static pg_boolean have_saved_ref (style_info_ptr stylebase, short current_index)
{
   register style_info_ptr    styles;
   memory_ref              current_embed; 
   short                index;
   
   if (!current_index)
      return   FALSE;
   
   styles = stylebase;
   current_embed = styles[current_index].embed_object;

   for (index = 0; index < current_index; ++index)
      if (styles[index].embed_object == current_embed)
         return   TRUE;
   
   return   FALSE;
}


/* pack_embed_ref sends all the data in ref to the walker (to write it to a file) */

static void pack_embed_ref (pack_walk_ptr walker, embed_ref ref)
{
   pg_embed_ptr            embed_ptr;
   long                 ref_id;

   ref_id = (long)ref;
   pgPackNum(walker, long_data, ref_id);     // So I know where it goes on READ
   
   embed_ptr = UseMemory(ref);

   pgPackNum(walker, long_data, CURRENT_EMBED_VERSION);
   pgPackNum(walker, long_data, embed_ptr->type);
   pgPackNum(walker, long_data, embed_ptr->width);
   pgPackNum(walker, long_data, embed_ptr->height);
   pgPackNum(walker, long_data, embed_ptr->descent);
   pgPackNum(walker, long_data, embed_ptr->draw_flags);
   pgPackNum(walker, long_data, embed_ptr->modifier);
   pgPackNum(walker, long_data, embed_ptr->user_refcon);
   pgPackNum(walker, long_data, (long)embed_ptr->data);

   pgPackNum(walker, short_data, (long)embed_ptr->top_extra);
   pgPackNum(walker, short_data, (long)embed_ptr->bot_extra);
   pgPackNum(walker, short_data, (long)embed_ptr->left_extra);
   pgPackNum(walker, short_data, (long)embed_ptr->right_extra);

#ifdef PG_SUBSETS
   pgPackNum(walker, short_data, (long)embed_ptr->alignment_flags);
#endif

   pgPackUnicodeBytes(walker, (pg_bits8_ptr)embed_ptr->uu.alt_data, ALT_SIZE * sizeof(pg_char));
   pgPackBytes(walker, (pg_bits8_ptr)&embed_ptr->border, sizeof(pg_border));

   if (embed_ptr->data) {
      
      switch (embed_ptr->type & EMBED_TYPE_MASK) {

         case embed_control:
#ifdef MAC_PLATFORM
            pgPackNum(walker, long_data, GetControlReference((ControlRef)embed_ptr->data));
#endif
            break;

         case embed_bitmap:
            pack_pic_embed(walker, embed_ptr);
            pgPackBitMap (walker, (bitmap_ref)embed_ptr->data);
            break;
            
         case embed_polygon:
            pgPackPoly(walker, (poly_ref)embed_ptr->data);
            break;

         case embed_pg_link:
            break;

         case embed_mac_pict:
            pack_pic_embed(walker, embed_ptr);
#ifdef MAC_PLATFORM
         {
            Handle         the_pict;
            
            the_pict = (Handle)embed_ptr->data;
            HLock(the_pict);
            pgPackBytes(walker, (pg_bits8_ptr)*the_pict, GetHandleSize(the_pict));
            HUnlock(the_pict);
         }
#else
            pgPackBytes(walker, UseMemory((memory_ref)embed_ptr->data),
                  GetByteSize((memory_ref)embed_ptr->data));
            UnuseMemory((memory_ref)embed_ptr->data);
#endif
            break;

         case embed_meta_file:
#ifdef WINDOWS_PLATFORM
            {
            HANDLE         metabits;
            long        meta, metasize;
            void PG_FAR    *metaptr;

            pack_pic_embed(walker, embed_ptr);

            meta = (long)embed_ptr->data;          

#ifndef WIN16_COMPILE
            
            if (metasize = GetMetaFileBitsEx((HMETAFILE)meta, 0, NULL)) {
            
               metabits = GlobalAlloc(GMEM_MOVEABLE, metasize);
               metaptr = GlobalLock(metabits);
               GetMetaFileBitsEx((HMETAFILE)meta, metasize, (LPVOID)metaptr);
               pgPackBytes(walker, (pg_bits8_ptr)metaptr, metasize);
               GlobalUnlock(metabits);
               GlobalFree(metabits);
            }
#else    
            metabits = (HANDLE)GetMetaFileBits((HMETAFILE)meta);
            metasize = GlobalSize(metabits);
            metaptr = GlobalLock(metabits);
            pgPackBytes(walker, (pg_bits8_ptr)metaptr, metasize);
            GlobalUnlock(metabits);
            embed_ptr->data = (void PG_FAR *)SetMetaFileBits(metabits);
#endif
         }
#else
            pgPackBytes(walker, UseMemory((memory_ref)embed_ptr->data),
                  GetByteSize((memory_ref)embed_ptr->data));
            UnuseMemory((memory_ref)embed_ptr->data);
#endif
            break;

         case embed_pg_pict:
            pack_pic_embed(walker, embed_ptr);
            pgPackPict(walker, (picture_ref)embed_ptr->data);
            break;
         
         case embed_ole: // PDA:
            pgPackNum(walker, long_data, (long)embed_ptr->data);
            break;
         case embed_mac_vm_pict:
         case embed_os2_meta_file:
         case embed_dibitmap:
         case embed_wbitmap:
         case embed_gif_pict:
         case embed_jpeg_pict:
            pack_pic_embed(walker, embed_ptr);
         case embed_alternate_char:
         case embed_dynamic_string:
         case embed_user_data:
         case embed_bookmark_start:
         case embed_bookmark_end:
            if (embed_ptr->data)
            {
            pgPackBytes(walker, UseMemory((memory_ref)embed_ptr->data),
                  GetByteSize((memory_ref)embed_ptr->data));
            UnuseMemory((memory_ref)embed_ptr->data);
            }
            else
            {
               pgPackUnicodeBytes(walker, (pg_bits8_ptr)embed_ptr->uu.alt_data,
                  pgCStrLength((pg_c_string_ptr)embed_ptr->uu.alt_data) * sizeof(pg_char));
            }
            break;

         case embed_qt_movie:
            pack_pic_embed(walker, embed_ptr);
#ifdef MAC_PLATFORM
#endif
            break;

         case embed_qt_flat_movie:
            pack_pic_embed(walker, embed_ptr);
            break;

         case embed_time:
           case embed_date:
            pgPackNum(walker, long_data, embed_ptr->uu.date_info.time);
            pgPackNum(walker, short_data, (long)embed_ptr->uu.date_info.time_type);
            pgPackNum(walker, short_data, (long)embed_ptr->uu.date_info.format);
            break;

           case embed_cur_page_num:
           case embed_cur_sect_num:
            break;
            
         case embed_horiz_rule:
            pgPackNum(walker, short_data, (long)embed_ptr->uu.line_info.line_size);
            pgPackNum(walker, short_data, (long)embed_ptr->uu.line_info.line_shading);
            pgPackNum(walker, short_data, (long)embed_ptr->uu.line_info.line_alignment);
            pgPackNum(walker, short_data, (long)embed_ptr->uu.line_info.line_width_type);
            pgPackNum(walker, long_data, embed_ptr->uu.line_info.line_width);
            break;
      }
   }

   UnuseMemory(ref);
}


/* custom_data_callback calls the app's callback function to setup data to save to a file. */

static void custom_data_callback (paige_rec_ptr pg, pack_walk_ptr walker, pg_embed_ptr embed_ptr,
      embed_callback callback, memory_ref buffer_ref)
{
   long        saved_size;

   SetMemorySize(buffer_ref, 0);
   callback(pg, embed_ptr, embed_ptr->type & EMBED_TYPE_MASK, EMBED_WRITE_DATA,
         embed_ptr->user_refcon, (long)buffer_ref, 0);
   
   if (saved_size = GetMemorySize(buffer_ref)) {

      pgPackBytes(walker, UseMemory(buffer_ref), saved_size);
      UnuseMemory(buffer_ref);
   }  
}


/* Orients as best as possible an unknown embedded type's text */

static co_ordinate_ptr center_unknown_text(pg_embed_ptr item, graf_device_ptr port, pg_char_ptr text, short length, rectangle_ptr draw_bounds, co_ordinate_ptr top_left_pt)
{
   short width;
   short ascent;
   
#ifdef MAC_PLATFORM
   FontInfo          f_info;

   GetFontInfo(&f_info);
   ascent = f_info.leading;
#endif

#ifdef WINDOWS_PLATFORM
    ascent = 0;
#endif

   width = pgTextWidth (port, text, length);

   top_left_pt->h = (draw_bounds->top_left.h + ((draw_bounds->bot_right.h - draw_bounds->top_left.h) >> 1)) - (width >> 1);
   top_left_pt->v = (draw_bounds->top_left.v + ((draw_bounds->bot_right.v - draw_bounds->top_left.v) >> 1)) - ascent;
   
   return top_left_pt;
}


/* Pack the extra picture information for output */

static void pack_pic_embed(pack_walk_ptr walker, pg_embed_ptr embed_ptr)
{
   pgPackNum(walker, short_data, (long)embed_ptr->uu.pict_data.pict_transparent);
   pgPackNum(walker, short_data, (long)embed_ptr->uu.pict_data.scale_pict);
   pgPackNum(walker, long_data, embed_ptr->uu.pict_data.vert_scale);
   pgPackNum(walker, long_data, embed_ptr->uu.pict_data.horiz_scale);
   pgPackRect(walker, &embed_ptr->uu.pict_data.pict_crop);
   pgPackNum(walker, long_data, embed_ptr->uu.pict_data.mapping_mode);
   pgPackNum(walker, short_data, embed_ptr->uu.pict_data.twips_width);
   pgPackNum(walker, short_data, embed_ptr->uu.pict_data.twips_height);
   pgPackNum(walker, short_data, embed_ptr->uu.pict_data.twips_gwidth);
   pgPackNum(walker, short_data, embed_ptr->uu.pict_data.twips_gheight);
   pgPackNum(walker, short_data, embed_ptr->uu.pict_data.meta_ext_x);
   pgPackNum(walker, short_data, embed_ptr->uu.pict_data.meta_ext_y);
}

/* Unpack the extra picture information for input */

static void unpack_pic_embed(pack_walk_ptr walker, pg_embed_ptr embed_ptr)
{
   if (embed_ptr->version > EMBED_REVISION_1)
   {
      embed_ptr->uu.pict_data.pict_transparent = (short)pgUnpackNum(walker);
      embed_ptr->uu.pict_data.scale_pict = (short)pgUnpackNum(walker);
      embed_ptr->uu.pict_data.vert_scale = pgUnpackNum(walker);
      embed_ptr->uu.pict_data.horiz_scale = pgUnpackNum(walker);
      pgUnpackRect(walker, &embed_ptr->uu.pict_data.pict_crop);
      
      if (embed_ptr->version > EMBED_REVISION_2) {

         embed_ptr->uu.pict_data.mapping_mode = pgUnpackNum(walker);
         embed_ptr->uu.pict_data.twips_width = (short) pgUnpackNum(walker);
         embed_ptr->uu.pict_data.twips_height = (short) pgUnpackNum(walker);
         embed_ptr->uu.pict_data.twips_gwidth = (short) pgUnpackNum(walker);
         embed_ptr->uu.pict_data.twips_gheight = (short) pgUnpackNum(walker);
      }
      
      if (embed_ptr->version > EMBED_REVISION_3) {
      	 
      	 embed_ptr->uu.pict_data.meta_ext_x = (short)pgUnpackNum(walker);
      	 embed_ptr->uu.pict_data.meta_ext_y = (short)pgUnpackNum(walker);
      }
   }
}


/* unpack_altsize_text reads an ALT_SIZE string and makes necessary Unicode conversions. */

static void unpack_altsize_text (paige_rec_ptr pg, pack_walk_ptr walker, pg_char_ptr str)
{
#ifdef UNICODE

	pgUnpackPtrBytes(walker, (pg_bits8_ptr)str);
	
	if (pg->flags2 & UNICODE_SAVED)
		pgUnicodeToUnicode((pg_short_t PG_FAR *)str, ALT_SIZE + 1, FALSE);
	else
		pgBytesToUnicode((pg_bits8_ptr)str, ALT_SIZE, FALSE);
#else
	if (pg->flags2 & UNICODE_SAVED) {

		pg_char		temp_name[(ALT_SIZE * 2) + 2];
		
		pgFillBlock(temp_name, (ALT_SIZE * 2) + 2, 0);
		pgUnpackPtrBytes(walker, temp_name);
		
		pgUnicodeToUnicode((pg_short_t PG_FAR *)temp_name, ALT_SIZE + 1, FALSE);
		pgUnicodeToBytes((pg_short_t PG_FAR *)temp_name, ALT_SIZE);
		pgBlockMove(temp_name, str, ALT_SIZE);
	}
	else 
		pgUnpackPtrBytes(walker, str);
#endif
}


/************************** Windows-specific functions **************************/

#ifdef WINDOWS_PLATFORM

int CALLBACK metafile_enum_proc (HDC hdc, HANDLETABLE FAR *handles, METARECORD FAR *rec,
        int num_objects, long refcon);
        
/* draw_meta_file plays a meta file into a pre-created bitmap, then stamps those bits to
the target DC. The purpose of this method is to render all meta files the same regardless
of mapping mode, viewport extend and origin. */


static void draw_meta_file (paige_rec_ptr pg, HDC target_dc, pg_embed_ptr embed_ptr, RECT PG_FAR *target_rect)
{
   int map_mode;

   if((map_mode = (int)embed_ptr->uu.pict_data.mapping_mode) == 0)
      map_mode = MM_TEXT;

	if(map_mode == MM_TEXT)
   {
	   POINT			old_org;			
    
      SetWindowOrgEx(target_dc, -target_rect->left, -target_rect->top, &old_org);
	   EnumMetaFile(target_dc, (HMETAFILE)(long)embed_ptr->data,
                   (MFENUMPROC)metafile_enum_proc, 0);
      SetWindowOrgEx(target_dc, old_org.x, old_org.y, NULL);
   }
   else
   {
      rectangle r;
      RECT rect;
      pg_scale_factor scale = {{0, 0}, 0};
	   int dc_state = SaveDC(target_dc);
      
      SetMapMode(target_dc, map_mode);
      if(pg->flags & PRINT_MODE_BIT)
         scale.scale = ((long)HIWORD(pg->resolution)) +
                       ((long)GetDeviceCaps(target_dc, LOGPIXELSX) << 16);
      else
         scale = pg->port.scale;

      RectToRectangle(target_rect, &r);
      pgScaleRect(&scale, NULL, &r);
      RectangleToRect(&r, NULL, &rect);

      SetViewportExtEx(target_dc, rect.right - rect.left, rect.bottom - rect.top, NULL);
      SetViewportOrgEx(target_dc, rect.left, rect.top, NULL);

      PlayMetaFile(target_dc, (HMETAFILE)(long)embed_ptr->data);

      RestoreDC(target_dc, dc_state);
   }
}

int CALLBACK metafile_enum_proc (HDC hdc, HANDLETABLE FAR *handles, METARECORD FAR *rec,
        int num_objects, long refcon)
{
	switch (rec->rdFunction) {

		case META_SETWINDOWORG:
		case META_SETWINDOWEXT:
		case META_SETVIEWPORTORG:
		case META_SETVIEWPORTEXT:
		case META_OFFSETWINDOWORG:
		case META_SCALEWINDOWEXT:
		case META_OFFSETVIEWPORTORG:
		case META_SCALEVIEWPORTEXT:
		case META_SETMAPMODE:
			break;
		
		default:
		    PlayMetaFileRecord(hdc, handles, rec, (UINT)num_objects);
		    break;
    }
 
	return	TRUE;
}

#endif
// End of Windows-specif functions



/************************ Macintosh-specific functions **************************/

#ifdef MAC_PLATFORM

/* detach_control_from_window detaches the ControlHandle from the Window it
was created it. */

//Code change by Timothy Paustian Monday, May 8, 2000 9:07:05 PM
//Changed to Opaque call for Carbon
static void detach_control_from_window (ControlHandle ctl)
{
   #if ACCESSOR_CALLS_ARE_FUNCTIONS == 1
   //This is not called by Frontier so not a problem
   //Note that this disposes of all the embedded controls in this control, 
   //but I doubt this is a problem. Available since MacOS 8
   DisposeControl(ctl);
   #else
   WindowPeek        owner;
   ControlHandle     next_control, previous_control;
   
   if (!(owner = (WindowPeek) (**ctl).contrlOwner))
      return;

   next_control = owner->controlList;
   previous_control = NULL;

   while (next_control) {
      
      if (next_control == ctl) {
         
         if (previous_control)
            (**previous_control).nextControl = (**next_control).nextControl;
         else
            owner->controlList = (**next_control).nextControl;
         break;
      }
      
      previous_control = next_control;
      next_control = (**next_control).nextControl;
   }
  #endif
}


#endif
// End of Mac-specific functions


