/******************************************************************************/
/*                                                                            */
/* COPYRIGHT:                                                                 */
/* ----------                                                                 */
/* Copyright (C) International Business Machines Corp., 1994.                 */
/* Copyright:(C) Apple Computer, Inc., 1994                                   */
/*                                                                            */  
/* DISCLAIMER OF WARRANTIES:                                                  */
/* -------------------------                                                  */
/* The following [enclosed] code is sample code created by IBM                */
/* Corporation.  This sample code is not part of any standard IBM product     */
/* and is provided to you solely for the purpose of assisting you in the      */
/* development of your applications.  The code is provided "AS IS",           */
/* without warranty of any kind.  IBM shall not be liable for any damages     */
/* arising out of your use of the sample code, even if they have been         */
/* advised of the possibility of such damages.                                */
/*                                                                            */
/******************************************************************************/
/*                              
   File:    CntnrPrt.cpp

   Contains:   implementation of class ContainerPart

   Written by: Jason Crawford


   Change History (most recent first):

             4/30/94 JLC   simplify draw part
*/
#ifdef __OS2__
   #define INCL_BASE
   #define INCL_WIN
   #define INCL_GPI
   
   #include <stdlib.h>
   #include <builtin.h>
   #include <string.h>
#endif

#define INCL_ODALTPOINT
#define INCL_ODARBITRAT
#define INCL_ODCANVAS
#define INCL_ODCLIPBD
#define INCL_ODCMDDEFS
#define INCL_ODDGITMITR
#define INCL_ODDISPTCH
#define INCL_ODDOCUMENT
#define INCL_ODDRAFT
#define INCL_ODDRAFT
#define INCL_ODDRAGDROP
#define INCL_ODEVENTS
#define INCL_ODEXCEPT
#define INCL_ODFACET
#define INCL_ODFOCI
#define INCL_ODFOCUSSET
#define INCL_ODFRAME
#define INCL_ODINFO
#define INCL_ODISOSTRING
#define INCL_ODLIBRARYMANAGERCLASSES
#define INCL_ODLIBRARYMANAGERUTILITIES
#define INCL_ODLIMITS
#define INCL_ODMENUBAR
#define INCL_ODMENUS
#define INCL_ODNAMRSLVR
#define INCL_ODORDCOLL
#define INCL_ODPASCLSTR
#define INCL_ODPRTPRPAC
#define INCL_ODQUICKDRAW
#define INCL_ODRESOURCES
#define INCL_ODSEMTINFT
#define INCL_ODSEUTILS
#define INCL_ODSHAPE
#define INCL_ODSTDDEFS
#define INCL_ODSTDPROPS
#define INCL_ODSTDPROPS
#define INCL_ODSTDTYPES
#define INCL_ODSTORAGE
#define INCL_ODSTORAGEU
#define INCL_ODSUCURSOR
#define INCL_ODSUVIEW
#define INCL_ODTOOLUTILS
#define INCL_ODTRNSFORM
#define INCL_ODUNDO
#define INCL_ODWINDOW
#define INCL_ODWINSTAT
#define INCL_ODWINITER   // for the sake of FrameToWindow
#define INCL_ODXMPCTR
#define INCL_ODXMPMEM
#define INCL_ODXMPSESSN
#define INCL_ODXMPUTILS

#include "odinclud.h"

#ifndef _CNTNRPRT_
   #include "CntnrPrt.h"
#endif

HWND  ContainerPart::hwndArrangeSubMenu = 0;
HWND  ContainerPart::hwndEmbedSubMenu = 0;
HWND  ContainerPart::hwndColorSubMenu = 0;
HWND  ContainerPart::hwndFrameSubMenu = 0;
ULONG ContainerPart::ulRefCnt = 0;
   
//==============================================================================
// Local functions
//==============================================================================

XMPBoolean ValueOnClipboard(XMPSession* session);

//==============================================================================
// Constants
//==============================================================================

// #define SSREMOVEWORKAROUND

const XMPSShort kXMPBorderWidth = 5;
const XMPSShort kXMPHandleLenMultiplier = 3;
#ifdef __OS2__
   const RGBColor rgbGray =    { 198, 198, 198 };
   const RGBColor rgbRed =     { 0x00, 0x00, 255 };
   const RGBColor rgbGreen =   { 0x00, 255, 0x00 };
   const RGBColor rgbYellow =  { 0x00, 198, 198 };
   const RGBColor rgbBlue =    { 255, 0x00, 0x00 };
   const RGBColor rgbMagenta = { 198, 0x00, 198 };
   const RGBColor rgbCyan =    { 198, 198, 0x00 };
   const RGBColor rgbWhite =   { 255, 255, 255 };
   const RGBColor rgbBlack =   { 0x00, 0x00, 0x00 };
#endif


const XMPSShort kPaletteLeft = 0;
const XMPSShort kPaletteRight = 50;
const XMPSShort kPaletteTop = 0;
const XMPSShort kPaletteBottom = 200;

enum {
   kGray = 0,
   kRed,
   kGreen,
   kYellow,
   kBlue,
   kMagenta,
   kCyan,
   kWhite,
   kNumColors
};

const    XMPSShort   kMargin = 10;

const short    kSuspendResumeMessage = 0x01; // Resume vs. suspend mask
const short    kMouseMovedMessage    = 0xFA; // High byte mouse-moved event message

//==============================================================================
// Commands
//==============================================================================
   
const XMPCommandID cXMPColor = 2000;
const XMPCommandID cXMPGray = 2001;
const XMPCommandID cXMPRed = 2002;
const XMPCommandID cXMPGreen = 2003;
const XMPCommandID cXMPYellow = 2004;
const XMPCommandID cXMPBlue = 2005;
const XMPCommandID cXMPMagenta = 2006;
const XMPCommandID cXMPCyan = 2007;
const XMPCommandID cXMPWhite = 2008;
const XMPCommandID cXMPOtherColor = 2009;

#ifdef __OS2__

   // constants for drawing background grid
   #define YGRID 32
   #define XGRID 32

   const char *apszColors[] = { "Gray", "Red", "Green", "Yellow", 
                                "Blue", "Magenta", "Cyan", "White" };
   //JYS: 
   #define IDM_COLOR_BASE 2010
   const XMPCommandID cXMPMaxEmbedParts = 10;
   const long         cXMPMaxEmbedPartString = 16;
   const XMPCommandID cXMPEmbed = IDM_COLOR_BASE+1;
   const XMPCommandID cXMPEmbedCntnr =  IDM_COLOR_BASE+2;
   const XMPCommandID cXMPEmbedClock = IDM_COLOR_BASE+3;
   const XMPCommandID cXMPEmbedTest = IDM_COLOR_BASE+4;
   const XMPCommandID cXMPEmbedPart = IDM_COLOR_BASE+5;

   // These next 9 defines are just place holders so no one tries to use these 
   // numbers by mistake.
   const XMPCommandID cXMPEmbedPart1 = IDM_COLOR_BASE+6;
   const XMPCommandID cXMPEmbedPart2 = IDM_COLOR_BASE+7;
   const XMPCommandID cXMPEmbedPart3 = IDM_COLOR_BASE+8;
   const XMPCommandID cXMPEmbedPart4 = IDM_COLOR_BASE+9;
   const XMPCommandID cXMPEmbedPart5 = IDM_COLOR_BASE+10;
   const XMPCommandID cXMPEmbedPart6 = IDM_COLOR_BASE+11;
   const XMPCommandID cXMPEmbedPart7 = IDM_COLOR_BASE+12;
   const XMPCommandID cXMPEmbedPart8 = IDM_COLOR_BASE+13;
   const XMPCommandID cXMPEmbedPart9 = IDM_COLOR_BASE+14;

   char partStrings[cXMPMaxEmbedParts][cXMPMaxEmbedPartString];

   #define IDM_FRAME_BASE 2030
   const XMPCommandID cXMPFrame = IDM_FRAME_BASE + 1;
   const XMPCommandID cXMPFreeze = IDM_FRAME_BASE + 2;
   const XMPCommandID cXMPDefrost = IDM_FRAME_BASE + 3;
   
   const XMPCommandID cXMPArrange = IDM_ARRANGE;
   const XMPCommandID cXMPClear = IDM_CLEAR;
   const XMPCommandID cXMPMoveFront = IDM_MOVETOFRONT;
   const XMPCommandID cXMPMoveBack = IDM_MOVETOBACK;
   const XMPCommandID cXMPMoveFwd = IDM_MOVEFORWARD;
   const XMPCommandID cXMPMoveBkwd = IDM_MOVEBACKWARD;
#endif

   
//==============================================================================
// 'ternalization 
//==============================================================================
      
const XMPPropertyName  kXMPPropFrameInfo       = "ContainerPart:Property:FrameInfo";
const XMPPropertyName  kXMPPropMouseDownOffset = "ContainerPart:Property:MouseDownOffset";

const XMPValueType     kQDPoint         = "ContainerPart:Type:Point";
const XMPValueType     kXMPMacTPrintRec = "Macintosh:Type:TPrint Record";

const XMPType          kContainerPartPresNormal  = "ContainerPart:Presentation:Normal";
const XMPType          kContainerPartPresPalette = "ContainerPart:Presentation:Palette";

const XMPPropertyName  kPropExample = "ContainerPart:Property:Example";
const XMPType          kExample     = "ContainerPart:Type:Example";

//JYS:
static HWND hwndDesktopFrame = 0;

//==============================================================================
// Types
//==============================================================================


XMPBoolean RGBEqual(RGBColor a,RGBColor b)
{
    return (a.bRed == b.bRed &&
            a.bGreen == b.bGreen &&
            a.bBlue == b.bBlue);
}

XMPUShort DetermineNumberOfPagesinDoc(XMPFrame* frame, Rect page);

//==============================================================================
// ContainerPart
//==============================================================================
#ifdef __OS2__
   // Helper functions.  These are functions that 
   //  (1) were helped reduce the code changes when
   //      porting the code from Mac to OS/2.
   //  or
   //  (2) routines that we expect Apple to be
   //      add to the API shortly.


   HWND FrameToWindow( XMPFrame * frame, XMPSession * session )
   {   
      XMPFrame *tmpFrame;

      // Get the root frame
      while ((tmpFrame = frame->GetContainingFrame()) != kXMPNULL)
              frame = tmpFrame;
      
      // Iterate through the windows and find the window with the same root frame.
      //XMPSession * session = fPart->GetStorageUnit()->GetSession();
      XMPWindowState * windowState = session->GetWindowState();
      XMPWindowIterator* iter = windowState->CreateWindowIterator();
      XMPWindow* window = iter->First();
      while (iter->IsNotComplete() && (window->GetRootFrame() != frame)) {
              window = iter->Next();
      } /* endif */
      delete iter;
      
      // return the window
      if (window && (window->GetRootFrame() == frame))
              return window->GetPlatformWindow();
      else
              return (HWND)0   /*kXMPNULL*/;
   }

   void WinMBox( const char * msg )
   {
        WinMessageBox(HWND_DESKTOP, HWND_DESKTOP,
                   msg, /*Caption:*/"", /*id?*/5,
                   MB_OK | MB_ERROR
                   );
   }
   
   void InflateRect(Rect *rect, int cx, int cy)
   {
      rect->xRight += cx;
      rect->xLeft -= cx;
      rect->yBottom -= cy;
      rect->yTop += cy;
   }
   void OffsetRect(Rect *rect, int cx, int cy) 
   {
      rect->xRight += cx;
      rect->xLeft += cx;
      rect->yBottom += cy;
      rect->yTop += cy;
   }
#endif

//-------------------------------------------------------------------------
// DLL entry point:
//-------------------------------------------------------------------------
XMPPart* EXPENTRY CreatePart()
{
   return new ContainerPart();
}

//-------------------------------------------------------------------------
// constructor/destructor:
//-------------------------------------------------------------------------


// *** constructor ***

ContainerPart::ContainerPart()
{
   // JYS: Submenu
   ContainerPart::ulRefCnt++;
   

   fDisplayFrames = kXMPNULL;
   fEmbeddedFrames = kXMPNULL;
   fWindowID = 0;

   fFrameGroupIDCounter = 1;

   fContents = kXMPNULL;
   fSelection = kXMPNULL;

   fSelectRgn = kXMPNULL;
   fCornerHandleRgn = kXMPNULL;
   fEdgeHandleRgn = kXMPNULL;

   fEmbedMenu = kXMPNULL;
   fColorMenu = kXMPNULL;
   fFrameMenu = kXMPNULL;
   fMenuBar = kXMPNULL;
   fFocusSet = kXMPNULL;

   fSemtIntf = kXMPNULL;
   fTestDrawSU = kXMPNULL;
   
   fPalette = kXMPNULL;
   fPaletteUp = kXMPFalse;
   fPaletteWasShown = kXMPFalse;
   
   fLargeIcons = kXMPNULL;
   fSmallIcons = kXMPNULL;

   fSession = kXMPNULL;
}


// *** destructor ***

ContainerPart::~ContainerPart()
{
   //JYS: Submenu stuff
   if ( --ContainerPart::ulRefCnt) {
       WinDestroyWindow(ContainerPart::hwndArrangeSubMenu);
       WinDestroyWindow(ContainerPart::hwndFrameSubMenu);
       WinDestroyWindow(ContainerPart::hwndEmbedSubMenu);
       WinDestroyWindow(ContainerPart::hwndColorSubMenu);
   }
   //JYS: end of submenu

   if (fContents != kXMPNULL)
      delete fContents;
   if (fSelection != kXMPNULL)
      delete fSelection;
   if (fSelectRgn) GpiDestroyRegion(hpsMem, fSelectRgn);
   if (fCornerHandleRgn) GpiDestroyRegion(hpsMem, fCornerHandleRgn);
   if (fEdgeHandleRgn) GpiDestroyRegion(hpsMem, fEdgeHandleRgn);
   if (hpsMem) GpiDestroyPS(hpsMem);
   if (hdcMem) DevCloseDC(hdcMem);
   
   if (fMenuBar != kXMPNULL) {   
      fMenuBar->UnregisterCommand(cXMPGray);
      fMenuBar->UnregisterCommand(cXMPRed);
      fMenuBar->UnregisterCommand(cXMPGreen);
      fMenuBar->UnregisterCommand(cXMPYellow);
      fMenuBar->UnregisterCommand(cXMPBlue);
      fMenuBar->UnregisterCommand(cXMPMagenta);
      fMenuBar->UnregisterCommand(cXMPCyan);
      fMenuBar->UnregisterCommand(cXMPWhite);
      fMenuBar->UnregisterCommand(cXMPOtherColor);
      
      fMenuBar->RemoveMenu(4);
      fMenuBar->RemoveMenu(5);
      fMenuBar->RemoveMenu(6);
      
      fMenuBar->Release();
   }

   if (fDisplayFrames != kXMPNULL)
      delete fDisplayFrames;     // make sure it's empty first
   if (fEmbeddedFrames != kXMPNULL)
      delete fEmbeddedFrames;    // make sure it's empty first

   if (fSemtIntf != kXMPNULL) 
      delete fSemtIntf;
   
   XMPReleaseObject(fTestDrawSU);
}


// *** Initialize ***

void ContainerPart::InitPart(XMPStorageUnit* storageUnit)  // Override
{
   if (fInitialized)
      return;
   
   ImplBasePart::InitPart(storageUnit);
   
   this->CommonInitContainerPart();
   
   XMPStorageUnit* msu = this->GetStorageUnit();
   msu->AddProperty(kXMPPropContents)->AddValue(kXMPKindTestDraw);   
   fTestDrawSU = msu->GetDraft()->CreateStorageUnit();

   fTestDrawSU->AddProperty(kXMPPropDisplayFrames)->AddValue(kXMPIDs);        
   fTestDrawSU->AddProperty(kXMPPropEmbeddedFrames)->AddValue(kXMPIDs);       
   fTestDrawSU->AddProperty(kXMPPropFrameGroup)->AddValue(kXMPULong);         
   fTestDrawSU->AddProperty(kXMPPropContents)->AddValue(kXMPIDs);
}
 

void ContainerPart::InitPartFromStorage(XMPStorageUnit* storageUnit)   // Override
{
   if (fInitialized)
      return;
   
   ImplBasePart::InitPartFromStorage(storageUnit);
   
   this->CommonInitContainerPart();
   

   XMPStorageUnit* su;
   XMPStorageUnitRef aSURef;
   XMPFrame* frame;
   XMPShape* newShape;
   XMPULong offset, offsetLimit;
   XMPRgnHandle newRegion;
   MATRIXLF xform;
   XMPTransform* transform;

   XMPUnused(newRegion);
   XMPVolatile(frame);

   su = this->GetStorageUnit();
   su->Focus(kXMPPropContents,kXMPPosSame,kXMPKindTestDraw,1,kXMPPosFirstSib);
   su->GetValue(sizeof(XMPStorageUnitRef),&aSURef);
   fTestDrawSU = su->GetDraft()->GetStorageUnit(su->GetIDFromStorageUnitRef(aSURef));
   su = fTestDrawSU;

   su->Focus(kXMPPropDisplayFrames, kXMPPosSame, 0, 1, kXMPPosFirstSib);
   offsetLimit = su->GetSize();
   for (offset = 0; offset < offsetLimit; offset += sizeof(XMPStorageUnitRef))
   {
      su->SetOffset(offset);
      su->GetValue(sizeof(XMPStorageUnitRef), (XMPValue)&aSURef);
      
      TRY
      
         frame = su->GetDraft()->GetFrame(su->GetIDFromStorageUnitRef(aSURef));
         fDisplayFrames->AddLast((ElementType)frame);
         frame->SetDroppable(kXMPTrue);
         
      CATCH_ALL
      
         frame = kXMPNULL;
   
      ENDTRY
   }

   su->Focus(kXMPPropFrameGroup,kXMPPosSame,0,1,kXMPPosFirstSib);
   su->GetValue(sizeof(fFrameGroupIDCounter),(XMPValue)&fFrameGroupIDCounter);
   
   su->Focus(kXMPPropContents,kXMPPosSame,0,1,kXMPPosFirstSib); 
   offsetLimit = su->GetSize();
   newShape = new XMPShape();
   for (offset = 0; offset < offsetLimit;)
   {
      su->SetOffset(offset);
      su->GetValue(sizeof(XMPStorageUnitRef), (XMPValue)&aSURef);
      offset += sizeof(XMPStorageUnitRef);
      frame = su->GetDraft()->GetFrame(su->GetIDFromStorageUnitRef(aSURef));
      fEmbeddedFrames->AddLast((ElementType)frame);

      su->SetOffset(offset);
      su->GetValue(sizeof(xform), (XMPValue)&xform);
      offset += sizeof(xform);
      transform = new XMPTransform;
      transform->SetPlatformTransform(kXMPOS2PM, new MATRIXLF(xform));
      
      newShape->CopyFrom(frame->GetFrameShape());
      newShape->Transform(transform);
      RgnHandle newRegion = newShape->CopyQDRegion();
      fContents->AddLast((ElementType)(new Proxy(newRegion, frame, transform)));
   }
   delete newShape;
}

void ContainerPart::CommonInitContainerPart()
{
   fDisplayFrames = new OrderedCollection;
   fEmbeddedFrames = new OrderedCollection;

   RGBColor random;
   random.bRed = rand();
   random.bGreen = rand();
   random.bBlue = rand();
   fDefaultColor = random;


   fContents = new OrderedCollection;
   fSelection = new OrderedCollection;
   SIZEL sizlPage = {0, 0};
   hdcMem = DevOpenDC   (1 , OD_MEMORY, "*", 0L, NULL, (HDC)0) ;
   hpsMem = GpiCreatePS (1 , hdcMem, &sizlPage, PU_PELS      | GPIF_DEFAULT |
                                                GPIT_MICRO   | GPIA_ASSOC) ;

   fSelectRgn = GpiCreateRegion(hpsMem, 0, NULL);
   fCornerHandleRgn = GpiCreateRegion(hpsMem, 0, NULL);
   fEdgeHandleRgn = GpiCreateRegion(hpsMem, 0, NULL);

   fSession = this->GetStorageUnit()->GetSession();

   fSelectionFocus = fSession->Tokenize(kXMPSelectionFocus);
   fMenuFocus = fSession->Tokenize(kXMPMenuFocus);
   fKeyFocus = fSession->Tokenize(kXMPKeyFocus);
   
   fFocusSet = new XMPFocusSet();
   fFocusSet->InitFocusSet();
   fFocusSet->Add(fSelectionFocus);
   fFocusSet->Add(fMenuFocus);
   fFocusSet->Add(fKeyFocus);
}


void ContainerPart::LoadIcons()
{
}


// *** Release ***

void ContainerPart::Release()
{
   Proxy*      p;
   XMPFrame*   frame;
   
   XMPPersistentObject::Release();
   if (this->GetRefCount() == 0) {
      this->HidePalette();
      frame = (XMPFrame*) fDisplayFrames->First();
      while (frame != kXMPNULL) {
         fDisplayFrames->Remove(frame);
         XMPReleaseObject(frame);
         frame = (XMPFrame*) fDisplayFrames->First();
      }
      frame = (XMPFrame*) fEmbeddedFrames->First();
      while (frame != kXMPNULL) {
         p = ProxyForFrame(frame);
         fContents->Remove(p);
         fEmbeddedFrames->Remove(frame);
         XMPReleaseObject(frame);
         frame = (XMPFrame*) fEmbeddedFrames->First();
      }
      this->GetStorageUnit()->GetDraft()->ReleasePart(this);
   }
}

//-------------------------------------------------------------------------
// From DragAndDrop protocol
//-------------------------------------------------------------------------


// *** FullfillPromise ***

void ContainerPart::FulfillPromise(XMPStorageUnitView *promiseSUView)
{
   XMPUnused(promiseSUView);
}


// *** NotifyDropComplete ***

void ContainerPart::DropCompleted(XMPPart* destPart, XMPDropResult dropResult)
{
   XMPUnused(destPart);
   XMPUnused(dropResult);
}

void InvertDragHilite( XMPFacet* facet)
{
   HWND hwnd = facet->GetWindow()->GetPlatformWindow();
   hwnd = WinWindowFromID( hwnd, FID_CLIENT );
   HPS hps = DrgGetPS( hwnd );   // using this PS directly is hokey, but it'll do for now.
                                 //  eventually we'll need to change the root canvas and
                                 //  do an invalidate here rather than use the HPS directly.

   LONG mixOld = GpiQueryMix( hps );
   LONG colorOld = GpiQueryColor( hps);
   GpiSetMix( hps, FM_XOR );
   GpiSetColor( hps, CLR_TRUE ); 

   RECTL rectl;
   XMPShape *trackingShape = new XMPShape();
   trackingShape->CopyFrom( facet->GetFrame()->GetFrameShape());
   trackingShape->Transform(facet->GetWindowFrameTransform());
   #if 1
      SIZEL szl;
      szl.cx = 4;
      szl.cy = 4;
      /*LONG  APIENTRY*/ GpiFrameRegion( hps
                                   , trackingShape->GetQDRegion()
                                   , /*thickness:*/&szl);
      delete trackingShape;
   #endif

   GpiSetColor( hps, colorOld );
   GpiSetMix( hps, mixOld );

   DrgReleasePS(hps);
}


// *** DragEnter ***

MRESULT ContainerPart::DragEnter(XMPDragItemIterator* dragInfo,
                                 XMPFacet* facet,
                                 XMPPoint where)
{
   InvertDragHilite( facet );
   XMPUnused(dragInfo);
   XMPUnused(facet);
   XMPUnused(where);

   return MRFROM2SHORT (DOR_DROP, DO_UNKNOWN);

}


// *** DragWithin ***

MRESULT ContainerPart::DragWithin(XMPDragItemIterator* dragInfo,
                                  XMPFacet* facet,
                                  XMPPoint where)
{
   XMPUnused(dragInfo);
   XMPUnused(facet);
   XMPUnused(where);

   return MRFROM2SHORT (DOR_DROP, DO_UNKNOWN);
}


// *** DragLeave ***

void ContainerPart::DragLeave(XMPFacet* facet,
                              XMPPoint where)
{
   InvertDragHilite( facet );
   XMPUnused(facet);
   XMPUnused(where);
}


// *** Drop ***

XMPDropResult ContainerPart::Drop(XMPDragItemIterator *dropInfo,
                                  XMPFacet* facet,
                                  XMPPoint where)
{
   XMPDraft       *theDraft = this->GetStorageUnit()->GetDraft();
   XMPULong        attributes;
   DragReference   theDrag;
   Point           delta, dragPoint, dropPoint, pinnedPoint;
   MATRIXLF        newXTrans;
   short           mouseDownModifiers, mouseUpModifiers, movePart;
   XMPStorageUnit *dropSU, *newSU;
   XMPID           draggedFrameID;
   Proxy          *p;
   XMPFrame       *frameDroppedIn;
   XMPFrame       *newFrame = kXMPNULL;
   XMPPart        *newPart;
   XMPShape       *clipShape, *newFrameShape;
   XMPTransform   *newExternalXForm;
   XMPBoolean      canHandleDrop = kXMPFalse;

   InvertDragHilite( facet);  // presumably I've highlighted myself, so turn it off now.

   // Check to see if we can handle the info in this drag (right now only parts)
   for (dropSU = dropInfo->First(); dropSU; dropSU = dropInfo->Next())
      if (dropSU->Exists(kXMPPropPart, kXMPISOStr, 0))
         canHandleDrop = kXMPTrue;

   if (canHandleDrop == kXMPFalse)
         return kXMPDropFail;

   // Get the attributes for this drag
   attributes = fSession->GetDragAndDrop()->GetDragAttributes();
   theDrag = fSession->GetDragAndDrop()->GetDragReference();
   movePart = (attributes & kXMPdragIsInSourceFrame);

   XMPBoolean  notDone = kXMPTrue;
   for (dropSU = dropInfo->First(); dropSU && notDone; dropSU = dropInfo->Next())
   {
      if (movePart)
      {
         XMPPoint ms;
         // Get where drag started and tweak our own structures to do the move.
         ms = facet->GetWindowFrameTransform()->InvertPoint(mouseStart);
         delta.x = where.x - ms.x;
         delta.y = where.y - ms.y;

         dropSU->Focus(kXMPPropFrameInfo, kXMPPosUndefined, kXMPID, 0, kXMPPosFirstSib);
         dropSU->GetValue(sizeof(XMPID), (XMPValue) &draggedFrameID);
         p = this->ProxyForFrameID(draggedFrameID);

         // !!! this is a single-facet hack
         p->transform->MoveBy(delta);
         newExternalXForm = new XMPTransform;
         newExternalXForm->CopyFrom(p->transform);
         XMPFrameFacetIterator* facets = p->frame->CreateFacetIterator();
         facets->First()->ChangeExternalTransform(newExternalXForm);
         delete facets;

         this->UpdateProxyRegion(p);
         this->CreateProxySelectionBorder(p);

         notDone = kXMPFalse;
      }
      else
      {
         XMPDraftKey key;

         XMPVolatile(dropSU);
         XMPVolatile(key);

         TRY
            key = dropSU->GetDraft()->BeginClone(kXMPClonePaste);
            newSU = dropSU->CloneTo(key, theDraft, kXMPNULL);
            dropSU->GetDraft()->EndClone(key);

         CATCH_ALL
            dropSU->GetDraft()->AbortClone(key);
            RERAISE;
         ENDTRY

         XMPPoint newTransPt(10,10);
         if (dropSU->Exists(kXMPPropMouseDownOffset, kQDPoint, 0))
         {
            Point mdOffset = {0,0};
            newTransPt = where;

            dropSU->Focus(kXMPPropMouseDownOffset, kXMPPosUndefined, kQDPoint, 0, kXMPPosFirstSib);
            dropSU->GetValue(sizeof(Point), (XMPValue) &mdOffset);
            newTransPt.x -= mdOffset.x;
            newTransPt.y -= mdOffset.y;
         }

         newExternalXForm = new XMPTransform;
         newExternalXForm->MoveBy( newTransPt );

         if (dropSU->Exists(kXMPPropFrameShape, NULL, 0))
         {
            dropSU->Focus(kXMPPropFrameShape, kXMPPosUndefined, NULL, 1, kXMPPosUndefined);
            newFrameShape = new XMPShape;
            newFrameShape = newFrameShape->ReadShape(dropSU);
         }
         else
         {
            newFrameShape = kXMPNULL;     // use default size {80,80}
         }

         newPart = theDraft->GetPart(newSU->GetID());
         newSU->Release();

         frameDroppedIn = facet->GetFrame();

         // We should be internalizing the frame from the storage unit
         newFrame = this->CreateEmbeddedFrame(frameDroppedIn,
                                     newFrameShape,
                                     newExternalXForm,
                                     newPart,
                                     kXMPNullTypeToken,
                                     kXMPNullTypeToken,
                                     0,
                                     kXMPFalse);

         // *** Set the frame's part info.  This is a kludge because
         // *** some of the sample parts we have now save their content
         // *** to the frame's partinfo rather than  adding properties
         // *** for part content in the storage unit.  The frame's partinfo
         // *** data should not normally be copied on a drag/drop operation.
         // *** This code will be removed once the sample parts are fixed 
         // *** up to externalize properly.

         dropSU->Focus(kXMPPropPartInfo, kXMPPosUndefined, kXMPPartInfo, 0, kXMPPosUndefined);
         XMPStorageUnitView *suView = new XMPStorageUnitView();
         suView->InitStorageUnitView(dropSU, dropSU->CreateCursor());
         XMPInfoType fPartInfo = (XMPInfoType)(newPart->ReadPartInfo(newFrame, suView));
         newFrame->SetPartInfo(fPartInfo);
         delete suView;

         XMPFrameFacetIterator* facets = frameDroppedIn->CreateFacetIterator();
         for (XMPFacet* facet = facets->First(); facets->IsNotComplete(); facet = facets->Next())
         {
            XMPShape* clip = new XMPShape;
            clip->CopyFrom(newFrame->GetFrameShape());
            XMPTransform* xform = new XMPTransform;
            xform->CopyFrom(this->ProxyForFrame(newFrame)->transform);
            facet->CreateEmbeddedFacet(newFrame, clip, xform, kXMPNULL, kXMPFrameInFront);
         }
         delete facets;
         this->ClipEmbeddedFrames(frameDroppedIn);
         frameDroppedIn->Invalidate(NULL);

         newPart->Release();

         notDone = kXMPFalse;
      }
   }
   this->GetStorageUnit()->GetDraft()->SetChangedFromPrev();

   return kXMPDropSucceed;
}

//-------------------------------------------------------------------------
// From Embedding protocol
//-------------------------------------------------------------------------

// *** SetContainerProperties *** !!!

   // ********* for containing parts *********


// *** GetContainingPartProperties ***

XMPStorageUnit* ContainerPart::GetContainingPartProperties(XMPFrame* displayFrame)
{
XMPUnused(displayFrame);

   return kXMPNULL;
}


// *** CreateEmbeddedFramesIterator ***

XMPEmbeddedFramesIterator* ContainerPart::CreateEmbeddedFramesIterator()
{
   return new ContainerPartEmbeddedFramesIterator(this);
}


// *** CountEmbeddedFrames ***

XMPSLong ContainerPart::CountEmbeddedFrames()
{
   return GetEmbeddedFrames()->Count();
}

// *** GetEmbeddedFrames ***
// private - for use by iterator only

OrderedCollection* ContainerPart::GetEmbeddedFrames()
{
   return fEmbeddedFrames;
}

//-------------------------------------------------------------------------
// from Frame protocol:
//-------------------------------------------------------------------------

// *** AddDisplayFrame ***

void ContainerPart::AddDisplayFrame(XMPFrame* frame)
{

   if (frame->GetPart() == this)    // frame belongs to me
   {
      OrderedCollectionIterator  displayFramesIter(fDisplayFrames);
      XMPFrame* displayFrame = (XMPFrame*) displayFramesIter.First();
      if (displayFramesIter.IsNotComplete() == kXMPFalse) {
         OrderedCollectionIterator  contentsIter(fContents);
         Proxy*                  proxy;
         
         for (proxy = (Proxy*) contentsIter.First();
               contentsIter.IsNotComplete(); 
               proxy = (Proxy*) contentsIter.Next())
         {
            proxy->frame->SetContainingFrame(frame);
         }
      }

      // !!! do something with viewType and partInfo...
      PartInfoRec* pInfo = new PartInfoRec;
      pInfo->bgColor = fDefaultColor;
      //pInfo->bgClipRegion = frame->GetFrameShape()->CopyQDRegion();         // <12> was GetQDRegion()
      if (frame->IsRoot())
         pInfo->fNeedsActivating = kXMPTrue;

      frame->SetPartInfo((XMPInfoType) pInfo);
      fDisplayFrames->AddLast(frame);
      frame->IncrementRefCount();
      frame->SetDroppable(kXMPTrue);
      
      if (frame->GetViewType() == kXMPNullTypeToken)                 // if frame view is set don't change it
         frame->SetViewType(fSession->Tokenize(kXMPViewAsFrame));    // if not, make it viewasframe
      if (frame->GetPresentation() == kXMPNullTypeToken)             
         frame->SetPresentation(fSession->Tokenize(kContainerPartPresNormal));
      
      if (frame->GetContainingFrame() == kXMPNULL)
      {
         //Wrong place. this->ActivateFrame(frame);
      }
   }
   else
      THROW(kXMPErrInvalidFrame);

   // render in frame?
}

// *** AttachSourceFrame ***

void ContainerPart::AttachSourceFrame(XMPFrame* frame, XMPFrame* sourceFrame)
{
   if (fDisplayFrames->Contains(frame)
       && sourceFrame && fDisplayFrames->Contains(sourceFrame))
   {
      //RCR Note. ContainerPart does not currently store embedded
      // frames on a per-containing-frame basis (eg. in the part info
      // of each frame). Since we can't add to the embedded frames list
      // while iterating over it, we make a temporary list here
      
         OrderedCollection* embeddedFrames = new OrderedCollection;
         {
            OrderedCollectionIterator iter(fEmbeddedFrames);
            for (XMPFrame* embeddedFrame = (XMPFrame*) iter.First(); 
                  iter.IsNotComplete(); 
                  embeddedFrame = (XMPFrame*) iter.Next())
            {
               embeddedFrames->AddLast(embeddedFrame);
            }
         }
         
         OrderedCollectionIterator iter(embeddedFrames);
         for (XMPFrame* embeddedFrame = (XMPFrame*) iter.First(); 
               iter.IsNotComplete(); 
               embeddedFrame = (XMPFrame*) iter.Next())
         {
            if (embeddedFrame->GetContainingFrame() == sourceFrame)
            {
               XMPPart* embeddedPart = embeddedFrame->GetPart();
               XMPShape* newShape = new XMPShape();
               newShape->CopyFrom(embeddedFrame->GetFrameShape());
               XMPTransform* newXForm = new XMPTransform();
               newXForm->CopyFrom(this->ProxyForFrame(embeddedFrame)->transform);
            XMPFrame* newFrame =
               this->CreateEmbeddedFrame(frame, newShape, newXForm, embeddedPart,
                                    kXMPNullTypeToken, kXMPNullTypeToken,
                                    0, embeddedFrame->IsOverlaid());
            embeddedPart->AttachSourceFrame(newFrame, embeddedFrame);
            }
         }
         delete embeddedFrames; // Delete the copy
      }
   else
      THROW(kXMPErrInvalidFrame);
}


// *** RemoveDisplayFrame ***

void ContainerPart::RemoveDisplayFrame(XMPFrame* oldFrame)
{
   if (oldFrame != (fPalette ? fPalette->GetRootFrame() : kXMPNULL)) {
      if (fDisplayFrames->Contains(oldFrame))
      {
         OrderedCollection* embeddedFrames = new OrderedCollection;
         {
            OrderedCollectionIterator iter(fEmbeddedFrames);
            for (XMPFrame* embeddedFrame = (XMPFrame*) iter.First(); 
                  iter.IsNotComplete(); 
                  embeddedFrame = (XMPFrame*) iter.Next())
            {
               embeddedFrames->AddLast(embeddedFrame);
            }
         }
         OrderedCollectionIterator iter(embeddedFrames);
         for (XMPFrame* embeddedFrame = (XMPFrame*) iter.First(); 
               iter.IsNotComplete(); 
               embeddedFrame = (XMPFrame*) iter.Next())
         {
            if (embeddedFrame->GetContainingFrame() == oldFrame)
            {
               this->RemoveEmbeddedFrame(embeddedFrame);
            }
         }
         delete embeddedFrames; // Delete the copy
   
         fSession->GetArbitrator()->RelinquishFocusSet(fFocusSet,oldFrame);
   
         PartInfoRec* pInfo = (PartInfoRec*) oldFrame->GetPartInfo();
         oldFrame->SetPartInfo((XMPInfoType) kXMPNULL);
         delete pInfo;
         fDisplayFrames->Remove(oldFrame);
         oldFrame->Release();
         
         OrderedCollectionIterator dIter(fDisplayFrames);
         XMPFrame*   displayFrame = (XMPFrame*) dIter.First();
         if (dIter.IsNotComplete() == kXMPFalse) {
            this->DestroyPalette();
         }
      }
      else
         THROW(kXMPErrInvalidFrame);
   }

   // any display frames left?
}


// *** CloseDisplayFrame ***

void ContainerPart::CloseDisplayFrame(XMPFrame* frame)
{
   if (fDisplayFrames->Contains(frame))
   {
      OrderedCollection* embeddedFrames = new OrderedCollection;
      {
         OrderedCollectionIterator iter(fEmbeddedFrames);
         for (XMPFrame* embeddedFrame = (XMPFrame*) iter.First(); 
               iter.IsNotComplete(); 
               embeddedFrame = (XMPFrame*) iter.Next())
         {
            embeddedFrames->AddLast(embeddedFrame);
         }
      }
      
      OrderedCollectionIterator iter(embeddedFrames);
      for (XMPFrame* embeddedFrame = (XMPFrame*) iter.First(); 
            iter.IsNotComplete(); 
            embeddedFrame = (XMPFrame*) iter.Next())
      {
         if (embeddedFrame->GetContainingFrame() == frame)
         {
            embeddedFrame->Close();
            fEmbeddedFrames->Remove(embeddedFrame);
            XMPReleaseObject(embeddedFrame);
         }
      }
      delete embeddedFrames; // Delete the copy
      
      fSession->GetArbitrator()->RelinquishFocusSet(fFocusSet, frame);

      PartInfoRec* pInfo = (PartInfoRec*) frame->GetPartInfo();
      frame->SetPartInfo((XMPInfoType) kXMPNULL);
      delete pInfo;
      fDisplayFrames->Remove(frame);
      XMPReleaseObject(frame);
   }
   else
      THROW(kXMPErrInvalidFrame);
}


// *** FrameShapeChanged ***

void ContainerPart::FrameShapeChanged(XMPFrame* frame)
{
   if (fDisplayFrames->Contains(frame))
   {
      /// !!! should leave UsedShape and ActiveShape null to inherit FrameShape
   }
   else
      THROW(kXMPErrInvalidFrame);
}


// *** ViewTypeChanged ***

void ContainerPart::ViewTypeChanged(XMPFrame* frame)
{
   if (fDisplayFrames->Contains(frame))
      { /* !!! change viewType of frame */ }
   else
      THROW(kXMPErrInvalidFrame);
}


// *** PresentationChanged ***

void ContainerPart::PresentationChanged(XMPFrame* frame)
{
   if (fDisplayFrames->Contains(frame))
      { /* !!! change presentation of frame */ }
   else
      THROW(kXMPErrInvalidFrame);
}


// *** WritePartInfo ***

void ContainerPart::WritePartInfo(XMPPtr partInfo, XMPStorageUnitView* storageUnitView)
{
   if (partInfo)
   {
      storageUnitView->SetValue(sizeof(RGBColor), (XMPValue)&(((PartInfoRec*)partInfo)->bgColor));
      XMPBoolean needsActivating = ((PartInfoRec*)partInfo)->fNeedsActivating 
                           || ((PartInfoRec*)partInfo)->fIsActive;
      storageUnitView->SetValue(sizeof(XMPBoolean),
                           (XMPValue)&needsActivating);
   }
}

// *** ReadPartInfo ***

XMPPtr ContainerPart::ReadPartInfo(XMPFrame* frame, XMPStorageUnitView* storageUnitView)
{
   XMPUnused(frame);
   if (storageUnitView->GetSize())
   {
      PartInfoRec* partInfo = new PartInfoRec;
      
      storageUnitView->GetValue(sizeof(RGBColor),
                           (XMPValue)&(partInfo->bgColor));
      XMPBoolean needsActivating;
      storageUnitView->GetValue(sizeof(XMPBoolean),
                           (XMPValue)&(needsActivating));
      partInfo->fNeedsActivating = needsActivating;
                           
      return partInfo;
   }
   else
      return ((XMPPtr)kXMPNULL);
}


// *** Open ***

XMPID ContainerPart::Open(XMPFrame* frame)
{
   XMPWindow* window = kXMPNULL;

   if (frame) // Doing a View As Window
   {
      window = fSession->GetWindowState()->GetWindow(fWindowID);
      if (window)
         window->Select();
      else
      {
         window = this->CreateWindow(frame);
         fWindowID = window->GetID();
         window->Open();
         window->Show();
         window->Select();       
      }
   }
   else
   {
      window = this->CreateWindow(frame);
      fWindowID = window->GetID();
      window->Open();
      window->Show();
      window->Select();       
   }
   return window->GetID();
}

XMPWindow* ContainerPart::CreateWindow(XMPFrame* sourceFrame)
{
   Rect windRect;
   XMPPlatformWindow platformWindow = kXMPNULL;
   XMPWindow* window = kXMPNULL;
   
   platformWindow = fSession->CreatePlatformWindow();
   window =  fSession->GetWindowState()->CreateWindow(platformWindow, 
                                        (sourceFrame==kXMPNULL),  // Keeps draft open
                                        kXMPTrue,  // Is resizable
                                        kXMPFalse, // Is floating
                                        kXMPTrue,  // should save
                                     this, kXMPNullTypeToken, kXMPNullTypeToken, sourceFrame);
   return window;
}

   // ********* for containing parts *********

// *** CreateEmbeddedFrame ***

XMPFrame* ContainerPart::CreateEmbeddedFrame(XMPFrame* containingFrame,
                              XMPShape* frameShape,
                              XMPTransform* externalTransform,
                              XMPPart* embedPart,
                              XMPTypeToken viewType,
                              XMPTypeToken presentation,
                              XMPID frameGroupID,
                              XMPBoolean isOverlaid)
{
   XMPShape* newShape = NULL;

   // determine the group ID of the new frame
   XMPUShort gID = (XMPUShort)(frameGroupID == kNewGroup ? fFrameGroupIDCounter++ : frameGroupID);

   // decide what newShape should be - for now, give it what it asked for
   if (frameShape)
   {  
      newShape = frameShape;
   }
   else
   {  // no frameShape specified, use a default size
      XMPRect rect;
      rect.SetInt(0,0,200,200);
      newShape = new XMPShape;
      newShape->SetRectangle(&rect);
   };
   XMPShape *copyShape = new XMPShape();
   copyShape->CopyFrom(newShape);

   // create the new frame
   XMPFrame* newFrame = this->GetStorageUnit()->GetDraft()->
         CreateFrame(containingFrame, newShape, embedPart,
                  viewType, presentation, gID, kXMPFalse, isOverlaid);
   fEmbeddedFrames->AddFirst(newFrame);

   // Give the embedded frame a chance to change it's frame shape;
   newFrame->ChangeFrameShape(copyShape);

   // create a proxy to hold the embedded frame
   XMPShape* scratch = new XMPShape;
   scratch->CopyFrom(newFrame->GetFrameShape());
   scratch->Transform(externalTransform);
   RgnHandle scratchRgn = scratch->CopyQDRegion();
   delete scratch;
   
   Proxy* p = new Proxy(scratchRgn, newFrame, externalTransform);  // new for Facets
   // put proxy into contents, possibly adjusting other content
   fContents->AddFirst(p);

   return newFrame;
}
   
// *** RemoveEmbeddedFrame ***

void ContainerPart::RemoveEmbeddedFrame(XMPFrame* embeddedFrame)
{
   if (fEmbeddedFrames->Contains(embeddedFrame))
   {
      XMPFrame* containingFrame = embeddedFrame->GetContainingFrame();
      Proxy* p = ProxyForFrame(embeddedFrame);
      if (p)
      {  
         fContents->Remove(p);
         XMPDeleteObject(p->transform);
         GpiDestroyRegion(hpsMem, p->region);
         delete p;
      }
      fEmbeddedFrames->Remove(embeddedFrame);
      embeddedFrame->Remove();
      //this->GetStorageUnit()->GetDraft()->RemoveFrame(embeddedFrame);
      
      this->ClipEmbeddedFrames(containingFrame);

   }
   else
      THROW(kXMPErrInvalidFrame);
}

// *** RequestFrameShape ***

XMPShape* ContainerPart::RequestFrameShape(XMPFrame* embeddedFrame, XMPShape* frameShape)
{
   if (fEmbeddedFrames->Contains(embeddedFrame))
   {  // find frame proxy and fix cached region
      Proxy* p = ProxyForFrame(embeddedFrame);
      if (p)
      {
         XMPShape* scratch = new XMPShape();
         scratch->CopyFrom(frameShape);
         scratch->Transform(p->transform);
         p->region = scratch->CopyQDRegion();
         delete scratch;

         p->frame->Invalidate(kXMPNULL);
         p->frame->Invalidate(frameShape);

         if (fSelection->Contains(p)) { /* !!! fix highlighting */ };
         
         // fix clipping of obscured frames
         this->ClipEmbeddedFrames(embeddedFrame->GetContainingFrame());
      }
   }
   else
      THROW(kXMPErrInvalidFrame);

   return frameShape;
}

// *** UsedShapeChanged ***

void ContainerPart::UsedShapeChanged(XMPFrame* embeddedFrame)
{
   if (fEmbeddedFrames->Contains(embeddedFrame))
   {
      this->ClipEmbeddedFrames(embeddedFrame->GetContainingFrame());
   }
   else
      THROW(kXMPErrInvalidFrame);
}

// *** ProxyForFrame ***

Proxy* ContainerPart::ProxyForFrame(XMPFrame* frame)
{
   if (fEmbeddedFrames->Contains(frame))
   {
      Proxy* p;
      OrderedCollectionIterator i(fContents);
      for (p = (Proxy*) i.First(); i.IsNotComplete(); p = (Proxy*) i.Next())
         if (p->frame == frame) return p;
      return kXMPNULL;
   }
   else
      THROW(kXMPErrInvalidFrame);

   return kXMPNULL;
}


// *** ProxyForFrameID ***

Proxy* ContainerPart::ProxyForFrameID(XMPID frameID)
{
   XMPFrame* frame = this->GetStorageUnit()->GetDraft()->GetFrame(frameID);
   Proxy* p = this->ProxyForFrame(frame);
   frame->Release();
   return p;
}


//-------------------------------------------------------------------------
// From Facet protocol
//-------------------------------------------------------------------------

// *** FacetAdded ***

void ContainerPart::FacetAdded(XMPFacet* facet)
{
   XMPFrame* dispFrame = facet->GetFrame();
   if (!fDisplayFrames->Contains(dispFrame))
      THROW(kXMPErrInvalidFacet);

   XMPShape* workingClip = new XMPShape;
   workingClip->InitShape();
   facet->SetPartInfo((XMPInfoType)workingClip);

   OrderedCollectionIterator frames(fEmbeddedFrames);
   for (XMPFrame* frame = (XMPFrame*) frames.First(); 
         frames.IsNotComplete(); 
         frame = (XMPFrame*) frames.Next())
   {
      if (frame->GetContainingFrame() == dispFrame)
      {
         XMPShape* clip = new XMPShape;
         clip->CopyFrom(frame->GetFrameShape());
         XMPTransform* xform = new XMPTransform;
         xform->CopyFrom(this->ProxyForFrame(frame)->transform);
         facet->CreateEmbeddedFacet(frame,
                              clip, xform,
                              kXMPNULL, kXMPFrameBehind);
      }
   }
   this->ClipEmbeddedFacets(facet);
}

// *** FacetRemoved ***

void ContainerPart::FacetRemoved(XMPFacet* facet)
{
   XMPFrame* dispFrame = facet->GetFrame();
   if (!fDisplayFrames->Contains(dispFrame))
      THROW(kXMPErrInvalidFacet);
   
   OrderedCollection* children = new OrderedCollection;
   XMPFacetIterator* facets = facet->CreateFacetIterator(kXMPChildrenOnly, kXMPFrontToBack);
   for (XMPFacet* childFacet = facets->First();
      facets->IsNotComplete();
      childFacet = facets->Next())
   {
      children->AddLast(childFacet);
   }
   delete facets;
   OrderedCollectionIterator iter(children);
   for (childFacet = (XMPFacet*)iter.First();
         iter.IsNotComplete();
         childFacet = (XMPFacet*)iter.Next())
   {
      facet->RemoveFacet(childFacet);
      delete childFacet;
   }
   delete (XMPShape*)facet->GetPartInfo();
   delete children;
}

// *** ClipShapeChanged ***

void ContainerPart::ClipShapeChanged(XMPFacet* facet)
{
   if (fDisplayFrames->Contains(facet->GetFrame()))
   {
      this->ClipEmbeddedFacets(facet);
   }
   else
      THROW(kXMPErrInvalidFacet);
}

// *** ExternalTransformChanged ***

void ContainerPart::ExternalTransformChanged(XMPFacet* facet)
{
XMPUnused(facet);
   // !!!
}

//-------------------------------------------------------------------------
// From Imaging protocol
//-------------------------------------------------------------------------

// *** Draw ***

void ContainerPart::Draw(XMPFacet* facet, XMPShape* invalShape)
{
   XMPBoolean drawing = facet->GetCanvas()->IsDynamic();
   
   XMPUnused(invalShape);

   XMPFrame* displayFrame = facet->GetFrame();
   if (fDisplayFrames->Contains(displayFrame))  
   {
      // transform grafport

      HPS hpsDraw = facet->GetCanvas()->GetPlatformCanvas();
      GpiSavePS(hpsDraw);


      PartInfoRec* pInfo = (PartInfoRec*) displayFrame->GetPartInfo();

      RgnHandle frameRgn = displayFrame->GetFrameShape()->GetQDRegion();
      Rect frameRect;
      GpiQueryRegionBox(hpsDraw, frameRgn, &frameRect);

      // set up clipping
      RgnHandle saveClip;
      XMPShape* clipShape = new XMPShape;
      clipShape->CopyFrom(facet->GetAggregateClipShape());
      clipShape->Transform(facet->GetContentTransform());
      clipShape->Subtract((XMPShape*)facet->GetPartInfo());
      RgnHandle clip = clipShape->GetQDRegion();
      GpiSetClipRegion(hpsDraw, clip, &saveClip);
      
      if (facet->GetFrame()->GetPresentation() == fSession->Tokenize(kContainerPartPresNormal))
      {
         XMPTypeToken curView = facet->GetFrame()->GetViewType();
         
         if (curView == fSession->Tokenize(kXMPViewAsFrame))
         {
            // paint the background
         
            GpiResetPS(hpsDraw, GRES_ATTRS);
      
            this->SetGrafPortOrigin(facet);

            RGBColor bgRGB = pInfo->bgColor;
            ULONG ulRGB = (bgRGB.bRed << 16) + (bgRGB.bGreen << 8) + bgRGB.bBlue;
            ULONG lColorIndex = GpiQueryColorIndex(hpsDraw, 0, ulRGB);
            GpiSetColor(hpsDraw, lColorIndex);
            GpiPaintRegion(hpsDraw, invalShape->GetQDRegion());

            GpiSetLineType(hpsDraw, LINETYPE_DOT);
            GpiSetColor(hpsDraw, CLR_BLACK);

            POINTL ptl;
            //for (int i = 0; i < 2; i++) {
               for (int y = YGRID / 2; y < frameRect.yTop; y += YGRID) {
                  ptl.y = y;
                  ptl.x = 0;
                  GpiMove(hpsDraw, &ptl);
                  ptl.x = frameRect.xRight;
                  GpiLine(hpsDraw, &ptl);
               }
         
               for (int x = XGRID / 2; x < frameRect.xRight; x += XGRID) {
                  ptl.x = x;
                  ptl.y = 0;
                  GpiMove(hpsDraw, &ptl);
                  ptl.y = frameRect.yTop;
                  GpiLine(hpsDraw, &ptl);
               }
            //}
            // highlite the selected frame.

            if (displayFrame == fSession->GetArbitrator()->GetFocusOwner(fSelectionFocus))
            {  // drawing active frame - highlight selection
               this->HighlightSelection(facet);
            }
            else
            {  // drawing inactive frame - do nothing for now
            }
         }
         else if (curView == fSession->Tokenize(kXMPViewAsLargeIcon))
         {
            /* Render as large Icon */
         }
         else if (curView == fSession->Tokenize(kXMPViewAsSmallIcon))
         {
            /* Render as small icon */
         }
      }
      else if ((drawing) && (facet->GetFrame()->GetPresentation() == fSession->Tokenize(kContainerPartPresPalette)))
      {
         /* Render palette frame */
      }
      GpiRestorePS(hpsDraw, -1);
   
      GpiSetClipRegion(hpsDraw, 0, &saveClip);
      delete clipShape;
   }
   else
   {
      // !!! signal error: invalid frame
   }
}

// *** HighlightSelection ***

void ContainerPart::HighlightSelection(XMPFacet *facet)
{
   switch (fSelection->Count())
   {
      case 0:     // no selection
         break;

      case 1:     // single selection
         {  OrderedCollectionIterator i(fSelection);
            this->HighlightProxyBorder((Proxy*)i.First(), facet);
         };
         break;

      default: // multiple selection
         {  Proxy* p;
            OrderedCollectionIterator i(fSelection);
            for (p = (Proxy*) i.First(); i.IsNotComplete(); p = (Proxy*) i.Next())
            {  if (fSelection->Contains(p))
                  this->HighlightContentObject(p, facet);
            };
         };
   };
}


// *** CreateProxySelectionBorder ***

void ContainerPart::CreateProxySelectionBorder(Proxy* p)
{
   RECTL arcl[4], borderRect;
   HRGN tempRgn = GpiCreateRegion(hpsMem, 0, NULL);

   // -- selection border region --

   GpiQueryRegionBox(hpsMem, p->region, arcl);
   GpiSetRegion(hpsMem, tempRgn, 1, arcl);
   GpiSetRegion(hpsMem, fSelectRgn, 1, arcl);
   InsetRgn(hpsMem, fSelectRgn, -kXMPBorderWidth, -kXMPBorderWidth);
   GpiCombineRegion(hpsMem, fSelectRgn, fSelectRgn, tempRgn, CRGN_XOR);

   GpiQueryRegionBox(hpsMem, fSelectRgn, &borderRect);

   // -- corner region --

   LONG handleWidth = (kXMPHandleLenMultiplier * kXMPBorderWidth);
   arcl[1] = arcl[0] = borderRect;
   arcl[0].xLeft += handleWidth;
   arcl[0].xRight -= handleWidth;                                
   arcl[1].yBottom +=  handleWidth;                                
   arcl[1].yTop -=  handleWidth;                                
   GpiCombineRegion(hpsMem, fCornerHandleRgn, fSelectRgn, 0, CRGN_COPY);
   GpiSetRegion(hpsMem, tempRgn, 2, arcl);
   GpiCombineRegion(hpsMem, fCornerHandleRgn, fCornerHandleRgn, tempRgn, CRGN_DIFF);

   // -- edge region --

   arcl[0] = arcl[1] = arcl[2] = arcl[3] = borderRect;
   arcl[0].xRight -= (borderRect.xRight - borderRect.xLeft) / 2;
   arcl[0].yTop -= (borderRect.yTop - borderRect.yBottom) / 2;
   arcl[1].xLeft = arcl[0].xRight + 1;
   arcl[1].yTop = arcl[0].yTop;
   arcl[2].xLeft = arcl[0].xRight + 1;
   arcl[2].yBottom = arcl[0].yTop + 1;
   arcl[3].xRight = arcl[0].xRight;
   arcl[3].yBottom = arcl[0].yTop + 1;

   if ((borderRect.xRight - borderRect.xLeft) >= (5 * kXMPHandleLenMultiplier * kXMPBorderWidth)) 
   {
      /* top and bottom handles */

      LONG hdist = (kXMPHandleLenMultiplier * kXMPBorderWidth) / 2;
      arcl[0].xRight -= hdist;
      arcl[1].xLeft += hdist;
      arcl[2].xLeft = arcl[1].xLeft;
      arcl[3].xRight = arcl[0].xRight;
   }

   if ((borderRect.yTop - borderRect.yBottom) >= (5 * kXMPHandleLenMultiplier * kXMPBorderWidth)) 
   {
      /* left and right handles */

      LONG vdist = (kXMPHandleLenMultiplier * kXMPBorderWidth) / 2;
      arcl[0].yTop -= vdist;
      arcl[1].yTop = arcl[0].yTop;
      arcl[2].yBottom += vdist;
      arcl[3].yBottom = arcl[2].yBottom;
   }
   GpiCombineRegion(hpsMem, fEdgeHandleRgn, fSelectRgn, 0, CRGN_COPY);
   GpiSetRegion(hpsMem, tempRgn, 4, arcl);
   GpiCombineRegion(hpsMem, fEdgeHandleRgn, fEdgeHandleRgn, tempRgn, CRGN_DIFF);

#if 0
   // clip by obscuring frames

   Proxy* q;
   OrderedCollectionIterator i(fContents);
   XMPBoolean above = true;
   for (q = (Proxy*) i.First(); i.IsNotComplete(); q = (Proxy*) i.Next())
   {  if (p == q) {above = false; continue;}
      if (!above) {continue;}
      GpiCombineRegion(hpsMem, fSelectRgn, fSelectRgn, q->region, CRGN_DIFF);
      GpiCombineRegion(hpsMem, fCornerHandleRgn, fCornerHandleRgn, q->region, CRGN_DIFF);
      GpiCombineRegion(hpsMem, fEdgeHandleRgn, fEdgeHandleRgn, q->region, CRGN_DIFF);
   };
#endif
   GpiDestroyRegion(hpsMem, tempRgn);

   // clip path from obscured frames
   this->ClipEmbeddedFrames(p->frame->GetContainingFrame());

}


// *** HighlightProxyBorder ***
void ContainerPart::HighlightProxyBorder(Proxy* p, XMPFacet *facet)
{
   XMPUnused(p);

   HPS hps = facet->GetCanvas()->GetPlatformCanvas();

   RgnHandle shapeRgn = GpiCreateRegion(hps, 0, NULL);
   Rect rect;
   XMPPoint pt(0,0);
   SIZEL sizlThickness = {1, 1};

   pt = facet->GetContentTransform()->TransformPoint(pt);
   POINTL ptl = pt.AsPOINTL();

   // draw frame border
   GpiSavePS(hps);
   GpiSetPattern(hps, PATSYM_DENSE5);
   GpiSetColor(hps, CLR_BLACK);

   GpiCombineRegion(hps, shapeRgn, fSelectRgn, 0, CRGN_COPY);
   GpiOffsetRegion(hps, shapeRgn, &ptl);
   GpiPaintRegion(hps, shapeRgn);

   GpiSetPattern(hps, PATSYM_SOLID);
   GpiSetColor(hps, CLR_WHITE);
   GpiCombineRegion(hps, shapeRgn, fCornerHandleRgn, 0, CRGN_COPY);
   GpiOffsetRegion(hps, shapeRgn, &ptl);
   GpiPaintRegion(hps, shapeRgn);
   GpiSetColor(hps, CLR_BLACK);
   GpiFrameRegion(hps, shapeRgn, &sizlThickness);

   GpiSetColor(hps, CLR_WHITE);
   GpiCombineRegion(hps, shapeRgn, fEdgeHandleRgn, 0, CRGN_COPY);
   GpiOffsetRegion(hps, shapeRgn, &ptl);
   GpiPaintRegion(hps, shapeRgn);
   GpiSetColor(hps, CLR_BLACK);
   GpiFrameRegion(hps, shapeRgn, &sizlThickness);

   GpiDestroyRegion(hps, shapeRgn);
   GpiRestorePS(hps, -1);
}


// *** HighlightContentObject ***
void ContainerPart::HighlightContentObject(Proxy* p, XMPFacet* facet)
{
   XMPUnused(p);
}


// *** ClipEmbeddedFrames ***
void ContainerPart::ClipEmbeddedFrames(XMPFrame* frame)
{
   XMPFrameFacetIterator* facets = frame->CreateFacetIterator();
   for (XMPFacet* facet = facets->First(); facets->IsNotComplete(); facet = facets->Next())
      this->ClipEmbeddedFacets(facet);
   delete facets;
}

// *** ClipEmbeddedFacets ***

void ContainerPart::ClipEmbeddedFacets(XMPFacet* facet)
{
   XMPTransform* wclipTrans = new XMPTransform;

   XMPShape* workingClip = new XMPShape();
   workingClip->CopyFrom(facet->GetClipShape());
   XMPShape* contentClip = (XMPShape*)facet->GetPartInfo();
   XMPRect emptyRect(0,0,0,0);
   contentClip->SetRectangle(&emptyRect);

   // let selection obscure embedded parts

   RgnHandle tempRgn;
   XMPShape* tempShape = new XMPShape;


   RECTL rclBox;
   LONG lComplexity = GpiQueryRegionBox(hpsMem, fSelectRgn, &rclBox);
   if (fSelectRgn &&  lComplexity != RGN_NULL && lComplexity != RGN_ERROR)
   {
      tempRgn = GpiCreateRegion(hpsMem, 0, NULL);
      GpiCombineRegion(hpsMem, tempRgn, fSelectRgn, 0, CRGN_COPY);
      GpiCombineRegion(hpsMem, tempRgn, tempRgn, fCornerHandleRgn, CRGN_OR);
      GpiCombineRegion(hpsMem, tempRgn, tempRgn, fEdgeHandleRgn, CRGN_OR);
      tempShape->SetPlatformShape(kXMPOS2PM, tempRgn);
      workingClip->Subtract(tempShape);
   }
   delete tempShape;

   wclipTrans->CopyFrom(facet->GetWindowFrameTransform());
   workingClip->Transform(wclipTrans);    // now in window coordinates
   
   XMPFacet* embFacet;  
   XMPFacetIterator* facets = facet->CreateFacetIterator(kXMPChildrenOnly, kXMPFrontToBack);
   for (embFacet = facets->First();
         facets->IsNotComplete();
         embFacet = facets->Next())
   {
      XMPShape* newClipShape = new XMPShape();
      newClipShape->CopyFrom(embFacet->GetFrame()->GetUsedShape());

      wclipTrans->CopyFrom(embFacet->GetWindowFrameTransform());
      wclipTrans->Invert();
      workingClip->Transform(wclipTrans);    // now in frame coordinates
      contentClip->Transform(wclipTrans);
      
      newClipShape->Intersect(workingClip);
      workingClip->Subtract(newClipShape);
      contentClip->Union(newClipShape);

      wclipTrans->Invert();
      workingClip->Transform(wclipTrans);    // now in window coordinates
      contentClip->Transform(wclipTrans);
      
      RECTL rcl;
      GpiQueryRegionBox(hpsMem, newClipShape->GetRegion(), &rcl);
      embFacet->ChangeClipShape(newClipShape);  // facet now owns shape storage
   }
   delete facets;
   
   // loop to clip for part content here
   

   delete wclipTrans;
// delete workingClip;
}

//-------------------------------------------------------------------------
// From Memory Management protocol
//-------------------------------------------------------------------------

// *** Purge ***

XMPSize ContainerPart::Purge(XMPSize size)
{
   return this->GetStorageUnit()->Purge(size);
}
     
//-------------------------------------------------------------------------
// From Part Activation protocol
//-------------------------------------------------------------------------

// *** RelinquishFocus ***

void ContainerPart::CommitRelinquishFocus(XMPTypeToken focus, 
                        XMPFrame* currentFrame,
                        XMPFrame* proposedFrame)
{
   XMPUnused(proposedFrame);

   this->FocusLost(focus, currentFrame);
   this->HidePalette();
}


// *** FocusAcquired ***

void ContainerPart::FocusAcquired(XMPTypeToken focus,
                        XMPFrame* newOwner)
{
   if (focus == fSelectionFocus) 
   {
      PartInfoRec* pInfo = (PartInfoRec*) newOwner->GetPartInfo();
      pInfo->fIsActive = kXMPTrue;
      
      this->ShowPalette();
   }
   else if (focus == fMenuFocus) 
      this->InstallMenus(newOwner);
}

// *** FocusLost *** 

void ContainerPart::FocusLost(XMPTypeToken focus,
                     XMPFrame* oldOwner)
{
   if (focus == fSelectionFocus) 
   {
      PartInfoRec* pInfo = (PartInfoRec*) oldOwner->GetPartInfo();
      pInfo->fIsActive = kXMPFalse;
      this->InvalidateSelection(oldOwner);
      this->EmptySelection();
      this->ClipEmbeddedFrames(oldOwner);
      
      this->HidePalette();
   }
   else if (focus == fMenuFocus) 
      this->RemoveMenus(oldOwner);
}

// *** InvalidateSelection ***

void ContainerPart::InvalidateSelection(XMPFrame* frame)
{
   XMPShape* selectShape = new XMPShape();
   RgnHandle invalidRgn = GpiCreateRegion(hpsMem, 0, NULL);
   GpiCombineRegion(hpsMem, invalidRgn, fSelectRgn, fCornerHandleRgn, CRGN_OR);
   GpiCombineRegion(hpsMem, invalidRgn, invalidRgn, fEdgeHandleRgn, CRGN_OR);
   selectShape->SetPlatformShape(kXMPOS2PM, invalidRgn);
   frame->Invalidate(selectShape);
   delete selectShape;
}

// *** EmptySelection ***

void ContainerPart::EmptySelection()
{
   for(XMPULong i = fSelection->Count(); i > 0; i--)
      fSelection->RemoveFirst();
   GpiSetRegion(hpsMem, fSelectRgn, 0, NULL);
   GpiSetRegion(hpsMem, fCornerHandleRgn, 0 ,NULL);
   GpiSetRegion(hpsMem, fEdgeHandleRgn, 0, NULL);
}

//-------------------------------------------------------------------------
// From Storage protocol
//-------------------------------------------------------------------------

// *** Externalize ***

void ContainerPart::Externalize()
{
   ImplBasePart::Externalize();
   
   XMPStorageUnit* su = fTestDrawSU;
   XMPStorageUnitRef aSURef;
   XMPULong offset;
   XMPFrame* frame;
   Proxy* proxy;
   OrderedCollectionIterator aIter(fDisplayFrames);
   OrderedCollectionIterator bIter(fEmbeddedFrames);
   OrderedCollectionIterator cIter(fContents);
   MATRIXLF xform;
 
   su->Focus(kXMPPropDisplayFrames,kXMPPosUndefined,0,1,kXMPPosFirstSib);
   #ifndef SSREMOVEWORKAROUND
      su->Remove();
      su->AddProperty(kXMPPropDisplayFrames);
      su->AddValue(kXMPIDs);
   #else
      XMPULong oldValueSize = su->GetSize();;
   #endif
   offset = 0;
   for (frame = (XMPFrame*)aIter.First(); aIter.IsNotComplete();
         frame = (XMPFrame*)aIter.Next(), offset+=sizeof(XMPStorageUnitRef))
   {
      frame->Externalize();
      aSURef = su->GetWeakStorageUnitRef(frame->GetStorageUnit());
      su->SetOffset(offset);
      su->SetValue(sizeof(XMPStorageUnitRef), (XMPValue)&aSURef);
   }
   #ifdef SSREMOVEWORKAROUND
      if (oldValueSize > offset) su->DeleteValue(oldValueSize-offset);
   #endif
 
   su->Focus(kXMPPropFrameGroup,kXMPPosUndefined,0,1,kXMPPosFirstSib);
   su->SetValue(sizeof(fFrameGroupIDCounter), (XMPValue)&fFrameGroupIDCounter);
   
   su->Focus(kXMPPropContents,kXMPPosUndefined,0,1,kXMPPosFirstSib);
   #ifndef SSREMOVEWORKAROUND
      su->Remove();
      su->AddProperty(kXMPPropContents);
      su->AddValue(kXMPIDs);
   #else
      oldValueSize = su->GetSize();;
   #endif
   offset = 0;
   for (proxy = (Proxy*)cIter.First();
         cIter.IsNotComplete(); 
         proxy = (Proxy*)cIter.Next())
   {
      proxy->frame->Externalize();

      aSURef = su->GetStrongStorageUnitRef(proxy->frame->GetStorageUnit());
      su->SetOffset(offset);
      su->SetValue(sizeof(XMPStorageUnitRef), (XMPValue)&aSURef);
      offset += sizeof(XMPStorageUnitRef);
      
      xform = *(PMATRIXLF)proxy->transform->GetPlatformTransform();
      su->SetOffset(offset);
      su->SetValue(sizeof(xform), (XMPValue)&xform);
      offset += sizeof(xform);
   }
   #ifdef SSREMOVEWORKAROUND
      if (oldValueSize > offset) su->DeleteValue(oldValueSize-offset);
   #endif
 
   su = this->GetStorageUnit();
   su->Focus(kXMPPropContents,kXMPPosSame,kXMPKindTestDraw,1,kXMPPosFirstSib);
   aSURef = su->GetStrongStorageUnitRef(fTestDrawSU);
   su->SetValue(sizeof(XMPStorageUnitRef),&aSURef);
}


// *** CloneInto ***

void ContainerPart::CloneInto(XMPDraftKey key, XMPStorageUnit* storageUnit, XMPStorageUnit* initiatingFrameSU)
{
   XMPStorageUnit*   su = this->GetStorageUnit();
   
   this->Externalize();
   XMPStorageUnit* destTestDrawSU = fTestDrawSU->CloneTo(key, storageUnit->GetDraft(), initiatingFrameSU); 
   
   storageUnit->AddProperty(kXMPPropContents);
   storageUnit->AddValue(kXMPKindTestDraw);
   XMPStorageUnitRef aSURef = storageUnit->GetStrongStorageUnitRef(destTestDrawSU);
   storageUnit->SetValue(sizeof(XMPStorageUnitRef),&aSURef);
      
   OrderedCollectionIterator contentsIter(fContents);
   Proxy* proxy;
   for (proxy = (Proxy*)contentsIter.First();
         contentsIter.IsNotComplete(); 
         proxy = (Proxy*)contentsIter.Next())
   {
      if ((initiatingFrameSU== kXMPNULL) ||
         (proxy->frame->GetContainingFrame()->GetStorageUnit() == initiatingFrameSU)) {
         XMPStorageUnit* toFrameSU = proxy->frame->CloneTo(key, storageUnit->GetDraft());
         toFrameSU->Release();
      }
   }
   destTestDrawSU->Release();
   
   su->CloneInto(key, storageUnit, initiatingFrameSU);
}

//-------------------------------------------------------------------------
// From UI Events protocol
//-------------------------------------------------------------------------


// *** HandleEvent *** !!!

XMPBoolean ContainerPart::HandleEvent(XMPEventData event, XMPFrame* frame,
      XMPFacet* facet)
{
   Point windowPoint;
   XMPBoolean handled = kXMPFalse;
   
   switch (event->what)
   {
      case kXMPEvtMouseDown:
         {
            if (event->msg == WM_BUTTON1DOWN || event->msg == WM_BUTTON2DOWN)
            {
               windowPoint = event->where;
               XMPPoint windowXMPPoint(windowPoint);
               handled = this->HandleMouseDown(facet, windowXMPPoint, event);
            }
         }
         break;
         
      case kXMPEvtMouseDownEmbedded:
         {
            if (event->msg == WM_BUTTON1DOWN || event->msg == WM_BUTTON2DOWN)
            {
               windowPoint = event->where;
               handled = this->HandleMouseDownInEmbeddedFrame(facet, 
                                 (XMPFacet*)event->ulExtra, windowPoint, event);
            }
         }
         break;
      
      case kXMPEvtMouseDownBorder:
         {
            if (event->msg == WM_BUTTON1DOWN || event->msg == WM_BUTTON2DOWN)
            {
               windowPoint = event->where;
               handled = this->HandleMouseDownInEmbeddedFrame(facet, 
                                 (XMPFacet*)event->ulExtra, windowPoint, event);
            }
         }
         break;
      
      case kXMPEvtMouseUp:
         break;
        
      case kXMPEvtMouseMove:
         {
            if (event->msg == WM_MOUSEMOVE) 
            {
               windowPoint = event->where;
               handled = this->HandleMouseMove(facet, windowPoint, event);

            } 
         }
         break;
      /* JYS: use accelerator instead!   
      case kXMPEvtKeyDown:
      case kXMPEvtAutoKey:
         handled = this->HandleKeyDown(frame, event);
         break;
      */
      case kXMPEvtActivate:
         handled = true; // actually ignored by dispatcher
         if (SHORT1FROMMP(event->mp1) != 0)
            this->ActivatingWindow(frame);
         else
            this->DeActivatingWindow(frame);
         break;
      
      case kXMPEvtKeyUp:
         break;
                  
      case kHighLevelEvent:
         break;

      case kXMPEvtMenu:
         handled = this->HandleMenuEvent(frame, event);
         break;
         
      default:
         return kXMPFalse;
   }
   return handled;
}

// *** HandleMouseDownDrag ***

XMPBoolean ContainerPart::HandleMouseDownDrag(XMPFacet* facet, Proxy* selection, XMPEventData event)
{
   XMPBoolean handled = kXMPFalse;

   XMPPart* destPart;
   XMPShape* scratch;
   Point translation = {0,0};
   Point mdOffset = {0,0};
   RgnHandle dragRgn, tempRgn;
   XMPID frameInfoData;
   XMPDragAndDrop*   dad;
   XMPStorageUnit*   unit;
   XMPDropResult dropResult;

   handled = kXMPTrue;
   if (event->msg==WM_BUTTON1DOWN) {
      ULONG fs = 0;
      TRACKINFO trackInfo;

      RgnHandle shapeRgn = selection->frame->GetFrameShape()->GetQDRegion();
      Rect shapeRect;
      GpiQueryRegionBox(hpsMem, shapeRgn, &shapeRect);

      Rect localRect = shapeRect;
      Point offset = selection->transform->GetQDOffset();
      OffsetRect(&localRect, offset.x, offset.y);

      memset(&trackInfo, 0, sizeof(trackInfo));

      trackInfo.fs = TF_MOVE;
      trackInfo.cxBorder = kXMPBorderWidth;
      trackInfo.cyBorder = kXMPBorderWidth;
      trackInfo.cxGrid = trackInfo.cyGrid = 1;
      trackInfo.cxKeyboard = trackInfo.cyKeyboard = 8;
      trackInfo.ptlMinTrackSize.x = 2 * kXMPBorderWidth * kXMPHandleLenMultiplier
                                    + 2;
      trackInfo.ptlMinTrackSize.y = 2 * kXMPBorderWidth * kXMPHandleLenMultiplier
                                    + 2;

      XMPShape *trackingShape = new XMPShape();
      trackingShape->CopyFrom(selection->frame->GetFrameShape());
      trackingShape->Transform(selection->transform);
      trackingShape->Transform(facet->GetWindowFrameTransform());
      GpiQueryRegionBox(hpsMem, trackingShape->GetQDRegion(), &trackInfo.rclTrack);
      Rect oldRect = trackInfo.rclTrack;
      InflateRect(&trackInfo.rclTrack, kXMPBorderWidth, kXMPBorderWidth);
      delete trackingShape;

      XMPShape *boundingShape = new XMPShape();
      boundingShape->CopyFrom(facet->GetFrame()->GetFrameShape());
      boundingShape->Transform(facet->GetWindowFrameTransform());
      GpiQueryRegionBox(hpsMem, boundingShape->GetQDRegion(), &trackInfo.rclBoundary);
      delete boundingShape;

      trackInfo.ptlMaxTrackSize.x = trackInfo.rclBoundary.xRight;
      trackInfo.ptlMaxTrackSize.y = trackInfo.rclBoundary.yTop;

      HWND hwnd = facet->GetWindow()->GetPlatformWindow();

      // Get the window handle of the client window to track over.
      // Shouldn't GetPlatformWindow return the client window handle? - CED

      hwnd = WinWindowFromID(hwnd, FID_CLIENT);

      if (WinTrackRect(hwnd, NULLHANDLE, &trackInfo)) {
         InflateRect(&trackInfo.rclTrack, -kXMPBorderWidth, -kXMPBorderWidth);

         //JPA: Does this really need to be a copy???
         XMPShape* oldShape = selection->frame->GetFrameShape()->Copy();

         XMPShape* newShape = new XMPShape;
         XMPRect* newBounds = new XMPRect(trackInfo.rclTrack);
         newBounds->Offset(-trackInfo.rclTrack.xLeft, -trackInfo.rclTrack.yBottom);
         newShape->SetRectangle(newBounds);
         Point transOffset = { trackInfo.rclTrack.xLeft - oldRect.xLeft,
                               trackInfo.rclTrack.yBottom - oldRect.yBottom };

         this->InvalEmbedFrameAfterResize(facet, selection, oldShape, newShape, transOffset);
      }
   } else if  (event->msg==WM_BUTTON2DOWN) {
      POINTL pts[13];
      XMPDragAndDrop*   dad = fSession->GetDragAndDrop();
      dad->Clear();
      {
         Rect shapeRect;
         #if 1
            XMPShape *trackingShape = new XMPShape();
            trackingShape->CopyFrom(selection->frame->GetFrameShape());
            trackingShape->Transform(selection->transform);
            trackingShape->Transform(facet->GetWindowFrameTransform());
            GpiQueryRegionBox(hpsMem, trackingShape->GetQDRegion(), &shapeRect);
            //Rect oldRect = shapeRect;
            //InflateRect(&shapeRect, kXMPBorderWidth, kXMPBorderWidth);
            shapeRect.xLeft   -= event->where.x;
            shapeRect.xRight  -= event->where.x;
            shapeRect.yBottom -= event->where.y;
            shapeRect.yTop    -= event->where.y;
            delete trackingShape;
         #endif
         // specify inner box
         pts[ 0].x = 0;   pts[0].y = 0;
         pts[ 1].x = shapeRect.xLeft ;  pts[ 1].y = shapeRect.yBottom;
         pts[ 2].x = shapeRect.xLeft ;  pts[ 2].y = shapeRect.yTop;
         pts[ 3].x = shapeRect.xRight;  pts[ 3].y = shapeRect.yTop;
         pts[ 4].x = shapeRect.xRight;  pts[ 4].y = shapeRect.yBottom;
         pts[ 5].x = shapeRect.xLeft ;  pts[ 5].y = shapeRect.yBottom;
         // do another slightly larger box
         pts[ 6].x = pts[ 1].x - 2   ;  pts[ 6].y = pts[ 1].y - 2;
         pts[ 7].x = pts[ 2].x - 2   ;  pts[ 7].y = pts[ 2].y + 2;
         pts[ 8].x = pts[ 3].x + 2   ;  pts[ 8].y = pts[ 3].y + 2;
         pts[ 9].x = pts[ 4].x + 2   ;  pts[ 9].y = pts[ 4].y - 2;
         pts[10].x = pts[ 5].x - 2   ;  pts[10].y = pts[ 5].y - 2;
         // return to inner box
         pts[11].x = shapeRect.xLeft ;  pts[11].y = shapeRect.yBottom;
         // return to origin to erase xor vector out to edge of box
         pts[12].x = 0;  pts[12].y = 0;
      }
      DRAGIMAGE dimg;         /* DRAGIMAGE structure                   */
      HBITMAP   hbm;          /* Bit-map handle                        */

      /*************************************************************/
      /* Initialize the DRAGIMAGE structure                        */
      /*************************************************************/
      dimg.cb = sizeof(DRAGIMAGE); /* Size control block           */

      // jlc changed the following code to use a polygon rather than
      //    a bitmap because he wanted to put something on the screen
      //    quickly and didn't want to take the time to code up a
      //    bitmap.  The following code should be changed to
      //    actually provide an outline of the object, or an icon.

      dimg.cptl        = 13;  // number of points
      dimg.hImage = (ULONG)(PPOINTL)pts;           /* Image handle passed to       */
                                   /* DrgDrag                      */
      dimg.sizlStretch.cx = 20L;   /* Size to stretch ico or bmp to*/
      dimg.sizlStretch.cy = 20L;
      dimg.fl = DRG_POLYGON;       /* Flags passed to DrgDrag      */
             //   DRG_STRETCH;       /* Stretch to size specified    */
                                   /* in sizlStretch               */
      dimg.cxOffset = 0;           /* Offset of the origin of      */
      dimg.cyOffset = 0;           /* the image from the pointer   */
                                   /* hotspot                      */

      // jlc - rest of the system needs to know that this frame is
      //       responsible for starting the drag.  We will need
      //       to code up some additional support later to insure
      //       that source messages like DM_DRAGOVERNOTIFY get
      //       back here.

      // *** Fill DAD SU with Props n' Vals
      unit = dad->GetStorageUnit();

      XMPDraft* fromDraft = this->GetStorageUnit()->GetDraft();
      XMPDraftKey key = fromDraft->BeginClone(kXMPCloneCut);
      selection->frame->GetPart()->CloneInto(key, unit, selection->frame->GetStorageUnit());
      fromDraft->EndClone(key);

      // *** save ID of frame being dragged

      frameInfoData = selection->frame->GetID();
      unit->AddProperty(kXMPPropFrameInfo)->AddValue(kXMPID);
      unit->SetValue(sizeof(XMPID), (XMPValue) &frameInfoData);

      // *** Save the frame's part info.  This is a kludge because
      // *** some of the sample parts we have now save their content
      // *** to the frame's partinfo rather than  adding properties
      // *** for part content in the storage unit.  The frame's partinfo
      // *** data should not normally be copied on a drag/drop operation.
      // *** This code will be removed once the sample parts are fixed 
      // *** up to externalize properly.

      PartInfoRec* pInfo = (PartInfoRec*) selection->frame->GetPartInfo();
      unit->AddProperty(kXMPPropPartInfo)->AddValue(kXMPPartInfo);
      XMPStorageUnitView *suView = new XMPStorageUnitView();
      suView->InitStorageUnitView(unit, unit->CreateCursor());
      XMPInfoType fPartInfo = selection->frame->GetPartInfo();
      selection->frame->GetPart()->WritePartInfo((XMPPtr)fPartInfo, suView);
      delete suView;

      // *** (optional) save offset between mousedown pt and topLeft pt of selection

      XMPFrameFacetIterator* faceti = selection->frame->CreateFacetIterator();
      Point topLeft = faceti->First()->GetWindowFrameTransform()->GetQDOffset();

      mouseStart.x = event->where.x;
      mouseStart.y = event->where.y;

      mdOffset = event->where;

      mdOffset.x -= topLeft.x;
      mdOffset.y -= topLeft.y;

      unit->AddProperty(kXMPPropMouseDownOffset)->AddValue(kQDPoint);
      unit->SetValue(sizeof(Point), (XMPValue) &mdOffset);

      // *** (optional) if dragging one frame, save its external transform (not aggregate)
                       
      unit->AddProperty(kXMPPropExternalTransform)->AddValue(kQDPoint);
      unit->SetValue(sizeof(Point), (XMPValue) &topLeft);

      // *** if dragging one frame, write its shape   [jpa]

      unit->AddProperty(kXMPPropFrameShape);
      selection->frame->GetFrameShape()->WriteShape(unit);


      selection->frame->SetDragging(kXMPTrue);

      dropResult = dad->StartDrag(facet->GetFrame(), 0, (XMPPtr)&dimg, &destPart, event);

      if (dropResult == kXMPDropSucceed)
      {
         if (destPart)
         {
            // The frame was dropped in the same document
            if (destPart == this)
            {
               // If this was a move to the same part
               facet->GetFrame()->Invalidate(NULL);
               selection->frame->SetDragging(kXMPFalse);
            }
            else
            {
               // This was a move to a different part
               XMPPart *oldPart = selection->frame->GetPart();
               oldPart->IncrementRefCount();
               facet->GetFrame()->Invalidate(NULL);
               this->InvalidateSelection(selection->frame);
               this->EmptySelection();

               OrderedCollection* tempFacets = new OrderedCollection;

               XMPFrameFacetIterator* facets = selection->frame->CreateFacetIterator();
               for (XMPFacet* f1 = facets->First();
                     facets->IsNotComplete();
                     f1 = facets->Next())
               {
                  tempFacets->AddLast(f1);
               }
               delete facets;

               OrderedCollectionIterator t(tempFacets);
               for (XMPFacet* f2 = (XMPFacet*)t.First();
                     t.IsNotComplete();
                     f2 = (XMPFacet*)t.Next())
               {
                  f2->GetContainingFacet()->RemoveFacet(f2);
                  XMPDeleteObject(f2);
               }
               delete tempFacets;

               this->RemoveEmbeddedFrame(selection->frame);
               this->GetStorageUnit()->GetDraft()->SetChangedFromPrev();
            }
         }
         else
         {
            // The frame was dropped in a different document
            selection->frame->SetDragging(kXMPFalse);
         }

         handled = kXMPTrue;
      }
      else
      {
         selection->frame->SetDragging(kXMPFalse);
      }
   } else {
   } /* endif */

   return handled;
}

// *** HandleMouseDownEdgeResize ***

XMPBoolean ContainerPart::HandleMouseDownEdgeResize(XMPFacet* facet,
      Proxy* selection, Point mouse)
{
   ULONG fs = 0;
   TRACKINFO trackInfo;

   RgnHandle shapeRgn = selection->frame->GetFrameShape()->GetQDRegion();
   Rect shapeRect;
   GpiQueryRegionBox(hpsMem, shapeRgn, &shapeRect);

   
   Rect localRect = shapeRect;
   Point offset = selection->transform->GetQDOffset();
   OffsetRect(&localRect, offset.x, offset.y);

   memset(&trackInfo, 0, sizeof(trackInfo));

   if      (mouse.y >= localRect.yTop)    trackInfo.fs = TF_TOP;
   else if (mouse.x <= localRect.xLeft)   trackInfo.fs = TF_LEFT;
   else if (mouse.y <= localRect.yBottom) trackInfo.fs = TF_BOTTOM;
   else if (mouse.x >= localRect.xRight)  trackInfo.fs = TF_RIGHT;

   trackInfo.cxBorder = kXMPBorderWidth;
   trackInfo.cyBorder = kXMPBorderWidth;
   trackInfo.cxGrid = trackInfo.cyGrid = 1;
   trackInfo.cxKeyboard = trackInfo.cyKeyboard = 8;
   trackInfo.ptlMinTrackSize.x = 2 * kXMPBorderWidth * kXMPHandleLenMultiplier
                                 + 2;
   trackInfo.ptlMinTrackSize.y = 2 * kXMPBorderWidth * kXMPHandleLenMultiplier
                                 + 2;

   XMPShape *trackingShape = new XMPShape();
   trackingShape->CopyFrom(selection->frame->GetFrameShape());
   trackingShape->Transform(selection->transform);
   trackingShape->Transform(facet->GetWindowFrameTransform());
   GpiQueryRegionBox(hpsMem, trackingShape->GetQDRegion(), &trackInfo.rclTrack);
   Rect oldRect = trackInfo.rclTrack;
   InflateRect(&trackInfo.rclTrack, kXMPBorderWidth, kXMPBorderWidth);
   delete trackingShape;

   XMPShape *boundingShape = new XMPShape();
   boundingShape->CopyFrom(facet->GetFrame()->GetFrameShape());
   boundingShape->Transform(facet->GetWindowFrameTransform());
   GpiQueryRegionBox(hpsMem, boundingShape->GetQDRegion(), &trackInfo.rclBoundary);
   delete boundingShape;

   trackInfo.ptlMaxTrackSize.x = trackInfo.rclBoundary.xRight;
   trackInfo.ptlMaxTrackSize.y = trackInfo.rclBoundary.yTop;

   HWND hwnd = facet->GetWindow()->GetPlatformWindow();

   // Get the window handle of the client window to track over.  
   // Shouldn't GetPlatformWindow return the client window handle? - CED

   hwnd = WinWindowFromID(hwnd, FID_CLIENT);


   if (WinTrackRect(hwnd, NULLHANDLE, &trackInfo)) {

      InflateRect(&trackInfo.rclTrack, -kXMPBorderWidth, -kXMPBorderWidth); 

      //JPA: Does this really need to be a copy???
      XMPShape* oldShape = selection->frame->GetFrameShape()->Copy();

      XMPShape* newShape = new XMPShape;
      XMPRect* newBounds = new XMPRect(trackInfo.rclTrack);
      newBounds->Offset(-trackInfo.rclTrack.xLeft, -trackInfo.rclTrack.yBottom);
      newShape->SetRectangle(newBounds);
      Point transOffset = { trackInfo.rclTrack.xLeft - oldRect.xLeft,
                            trackInfo.rclTrack.yBottom - oldRect.yBottom };
      
      this->InvalEmbedFrameAfterResize(facet, selection, oldShape, newShape, transOffset);
   }
   return kXMPTrue;
}

// *** GetSelectionRectLocal ***

Rect* ContainerPart::GetSelectionRectLocal(Proxy* selection)
{
   RgnHandle shapeRgn = selection->frame->GetFrameShape()->GetQDRegion();
   Rect shapeRect;
   GpiQueryRegionBox(hpsMem, shapeRgn, &shapeRect);
   
   Rect* localRect = new Rect(shapeRect);
   
   Point offset = selection->transform->GetQDOffset();
   OffsetRect(localRect, offset.x, offset.y);
   return localRect;
}


// *** HandleMouseDownCornerResize ***

XMPBoolean ContainerPart::HandleMouseDownCornerResize(XMPFacet* facet, Proxy* selection, Point mouse)
{

   ULONG fs = 0;
   TRACKINFO trackInfo;

   RgnHandle shapeRgn = selection->frame->GetFrameShape()->GetQDRegion();
   Rect shapeRect;
   GpiQueryRegionBox(hpsMem, shapeRgn, &shapeRect);
   
   Rect localRect = shapeRect;
   Point offset = selection->transform->GetQDOffset();
   OffsetRect(&localRect, offset.x, offset.y);

   memset(&trackInfo, 0, sizeof(trackInfo));

   Point center = {(localRect.xRight + localRect.xLeft) / 2 ,
                   (localRect.yBottom + localRect.yTop) / 2 };
   
   if      ((mouse.y <= center.y) && (mouse.x <= center.x)) trackInfo.fs = TF_BOTTOM | TF_LEFT;
   else if ((mouse.y <  center.y) && (mouse.x >  center.x)) trackInfo.fs = TF_BOTTOM | TF_RIGHT;
   else if ((mouse.y >  center.y) && (mouse.x <  center.x)) trackInfo.fs = TF_TOP | TF_LEFT;
   else if ((mouse.y >  center.y) && (mouse.x >  center.x)) trackInfo.fs = TF_TOP | TF_RIGHT;

   trackInfo.cxBorder = kXMPBorderWidth;
   trackInfo.cyBorder = kXMPBorderWidth;
   trackInfo.cxGrid = trackInfo.cyGrid = 1;
   trackInfo.cxKeyboard = trackInfo.cyKeyboard = 8;
   trackInfo.ptlMinTrackSize.x = 2 * kXMPBorderWidth * kXMPHandleLenMultiplier
                                 + 2;
   trackInfo.ptlMinTrackSize.y = 2 * kXMPBorderWidth * kXMPHandleLenMultiplier
                                 + 2;

   XMPShape *trackingShape = new XMPShape();
   trackingShape->CopyFrom(selection->frame->GetFrameShape());
   trackingShape->Transform(selection->transform);
   trackingShape->Transform(facet->GetWindowFrameTransform());
   GpiQueryRegionBox(hpsMem, trackingShape->GetQDRegion(), &trackInfo.rclTrack);
   Rect oldRect = trackInfo.rclTrack;
   InflateRect(&trackInfo.rclTrack, kXMPBorderWidth, kXMPBorderWidth);
   delete trackingShape;

   XMPShape *boundingShape = new XMPShape();
   boundingShape->CopyFrom(facet->GetFrame()->GetFrameShape());
   boundingShape->Transform(facet->GetWindowFrameTransform());
   GpiQueryRegionBox(hpsMem, boundingShape->GetQDRegion(), &trackInfo.rclBoundary);
   delete boundingShape;

   trackInfo.ptlMaxTrackSize.x = trackInfo.rclBoundary.xRight;
   trackInfo.ptlMaxTrackSize.y = trackInfo.rclBoundary.yTop;

   HWND hwnd = facet->GetWindow()->GetPlatformWindow();

   // Get the window handle of the client window to track over.  
   // Shouldn't GetPlatformWindow return the client window handle - CED

   hwnd = WinWindowFromID(hwnd, FID_CLIENT);

   if (WinTrackRect(hwnd, NULLHANDLE, &trackInfo)) {
      InflateRect(&trackInfo.rclTrack, -kXMPBorderWidth, -kXMPBorderWidth); 

      //JPA: Does this really need to be a copy???
      XMPShape* oldShape = selection->frame->GetFrameShape()->Copy();

      XMPShape* newShape = new XMPShape;
      XMPRect* newBounds = new XMPRect(trackInfo.rclTrack);
      newBounds->Offset(-trackInfo.rclTrack.xLeft, -trackInfo.rclTrack.yBottom);
      newShape->SetRectangle(newBounds);
      Point transOffset = { trackInfo.rclTrack.xLeft - oldRect.xLeft,
                            trackInfo.rclTrack.yBottom - oldRect.yBottom };
      
      this->InvalEmbedFrameAfterResize(facet, selection, oldShape, newShape, transOffset);
   }
   return kXMPTrue;
}

// *** InvalEmbedFrameAfterResize ***

void ContainerPart::InvalEmbedFrameAfterResize(   XMPFacet* facet, Proxy* selection,
                                 XMPShape* oldShape, XMPShape* newShape,
                                 Point transOffset)
{
   // invalidate old frame shape
   oldShape->Transform(selection->transform);
   facet->GetFrame()->Invalidate(oldShape);

   XMPShape* newShapeCopy = new XMPShape;
   newShapeCopy->CopyFrom(newShape);      // save to invalidate later

   selection->frame->ChangeFrameShape(newShape);   // already in frame coords

   // if the left or top side of the frame was adjusted, the transform will be updated
   if ((transOffset.x != 0) || (transOffset.y != 0)) {
      selection->transform->MoveBy(transOffset);

      // move facets for selected frame - this is slightly hackish
      XMPTransform* newTrans;
      XMPFacetIterator* facets =
         facet->GetWindow()->GetRootFacet()->CreateFacetIterator(kXMPTopDown, kXMPFrontToBack);
      for (XMPFacet* child = facets->First();
            facets->IsNotComplete(); child = facets->Next())
      {
         if (child->GetFrame() == selection->frame)
         {
            newTrans = new XMPTransform;
            newTrans->CopyFrom(selection->transform);
            child->ChangeExternalTransform(newTrans);
            facets->SkipChildren();
         }
      }
      delete facets;
   }

   // invalidate changed areas
   newShapeCopy->Transform(selection->transform);
   facet->GetFrame()->Invalidate(newShapeCopy);
   delete newShapeCopy;

   this->InvalidateSelection(facet->GetFrame());
   this->UpdateProxyRegion(selection);
   this->CreateProxySelectionBorder(selection);
   this->ClipEmbeddedFrames(facet->GetFrame());
   this->InvalidateSelection(facet->GetFrame());
   
   this->GetStorageUnit()->GetDraft()->SetChangedFromPrev();
}

// *** HandleMouseDown ***

XMPBoolean ContainerPart::HandleMouseDown(  XMPFacet* facet,
                              XMPPoint where,
                              XMPEventData event)
{
   if (!facet->GetWindow()->IsActive())
      facet->GetWindow()->Select();
   else
      this->ActivateFrame(facet->GetFrame());

   Point mouse = facet->GetWindowContentTransform()->InvertPoint(where).AsPOINTL();

   if (facet->GetWindow() == fPalette) {
      
      RgnHandle   bRgn = facet->GetFrame()->GetFrameShape()->GetQDRegion();
      Rect     bRect;
      XMPUShort   height;
      XMPUShort   whichColor;
      XMPBoolean  didHitMargin;
      
      GpiQueryRegionBox(hpsMem, bRgn, &bRect);

      height = ((bRect.yBottom - bRect.yTop) - kMargin - ((kNumColors-1) * kMargin) - kMargin) / kNumColors;
      
      whichColor = mouse.y / (kMargin + height);
      didHitMargin = ((mouse.y  % (kMargin + height)) < kMargin) || 
                  (mouse.x < kMargin) || 
                  ((mouse.x + kMargin) >  bRect.xRight);
      
      if (didHitMargin == kXMPFalse) {
         OrderedCollectionIterator aIter(fDisplayFrames);
         
         XMPFrame*   displayFrame;
         for (displayFrame = (XMPFrame*) aIter.First(); aIter.IsNotComplete(); displayFrame = (XMPFrame*) aIter.Next())
         {
            XMPFrameFacetIterator*  facets = displayFrame->CreateFacetIterator();
            for (XMPFacet* facet = (XMPFacet*) facets->First();
                  facets->IsNotComplete();
                  facet = (XMPFacet*) facets->Next())
            {
               if (facet->GetWindow() != fPalette)
               {
                  this->SetBGColor(displayFrame, whichColor);
                  displayFrame->Invalidate(kXMPNULL);
               }
            }
            delete facets;
         }
      }
   }
   else {
   
      OrderedCollectionIterator i(fSelection);
      Proxy* p = (Proxy*)i.First();
      
      if (GpiPtInRegion(hpsMem, fCornerHandleRgn, &mouse) == PRGN_INSIDE)
         return HandleMouseDownCornerResize(facet, p, mouse);
      else if (GpiPtInRegion(hpsMem, fEdgeHandleRgn, &mouse) == PRGN_INSIDE)
         return HandleMouseDownEdgeResize(facet, p, mouse);
      else if (GpiPtInRegion(hpsMem, fSelectRgn, &mouse) == PRGN_INSIDE)
         return HandleMouseDownDrag(facet, p, event);
      else
         switch (fSelection->Count())
         {
            case 0:
               break;
         
            case 1:
               this->InvalidateSelection(facet->GetFrame());
               this->EmptySelection();
               this->ClipEmbeddedFrames(facet->GetFrame());
               break;
               
            default:
               // !!! handle multiple selection
               break;
         }
   }

   return kXMPTrue;
}

// --- HandleMouseMove ---

XMPBoolean ContainerPart::HandleMouseMove(XMPFacet* facet, 
                                          XMPPoint where,
                                          XMPEventData event)
{
   XMPBoolean handled = kXMPFalse;
   RECTL rclBox;

   if (facet->GetWindow()->IsActive() && facet->GetWindow() != fPalette)
   {
      if (fSelection->Count() >= 1)
      {
         POINTL ptlMouse = facet->GetWindowContentTransform()->InvertPoint(where).AsPOINTL();

         if (GpiPtInRegion(hpsMem, fCornerHandleRgn, &ptlMouse) == PRGN_INSIDE)
         {
            GpiQueryRegionBox(hpsMem, fCornerHandleRgn, &rclBox);
            ptlMouse.x -= (rclBox.xLeft + rclBox.xRight) / 2;
            ptlMouse.y -= (rclBox.yBottom + rclBox.yTop) / 2;
            WinSetPointer(HWND_DESKTOP,
               WinQuerySysPointer(
                   HWND_DESKTOP,
                   (ptlMouse.x * ptlMouse.y > 0) ? SPTR_SIZENESW : SPTR_SIZENWSE,
                   FALSE));
            handled = kXMPTrue;
         }
         else if (GpiPtInRegion(hpsMem, fEdgeHandleRgn, &ptlMouse) == PRGN_INSIDE)
         {
            GpiQueryRegionBox(hpsMem, fCornerHandleRgn, &rclBox);
            ptlMouse.x -= (rclBox.xLeft + rclBox.xRight) / 2;
            ptlMouse.y -= (rclBox.yBottom + rclBox.yTop) / 2;
            WinSetPointer(HWND_DESKTOP,
               WinQuerySysPointer(
                   HWND_DESKTOP,
                   (abs(ptlMouse.x) > abs(ptlMouse.y)) ? SPTR_SIZEWE : SPTR_SIZENS,
                   FALSE));
            handled = kXMPTrue;
         }
         else if (GpiPtInRegion(hpsMem, fSelectRgn, &ptlMouse) == PRGN_INSIDE)
         {
            WinSetPointer(HWND_DESKTOP,
               WinQuerySysPointer( HWND_DESKTOP, SPTR_MOVE, FALSE));
            handled = kXMPTrue;
         }
      }
   }
   return handled;
}

// *** HandleMouseDownInEmbeddedFrame ***

XMPBoolean ContainerPart::HandleMouseDownInEmbeddedFrame(XMPFacet* container,
                                       XMPFacet* facet,
                                       XMPPoint where,
                                       XMPEventData event)
{
   XMPUnused(where);
   XMPUnused(event);

   this->ActivateFrame(container->GetFrame());

   Proxy* p = ProxyForFrame(facet->GetFrame());

   switch (fSelection->Count())
   {
      case 0:
         fSelection->AddFirst((ElementType)p);
         this->CreateProxySelectionBorder(p);
         this->ClipEmbeddedFrames(container->GetFrame());
         this->InvalidateSelection(container->GetFrame());
         break;

      case 1:
         this->InvalidateSelection(container->GetFrame());
         fSelection->RemoveFirst();
         fSelection->AddFirst((ElementType)p);
         this->CreateProxySelectionBorder(p);
         this->ClipEmbeddedFrames(container->GetFrame());
         this->InvalidateSelection(container->GetFrame());
         break;
         
      default:
         // !!! handle multiple selection
         break;
   }

   this->InvalidateSelection(container->GetFrame());

   return kXMPTrue;
}

// *** HandleKeyDown ***

XMPBoolean ContainerPart::HandleKeyDown(XMPFrame* focusFrame, XMPEventData event)
{
   // cursor keys? !!!

   XMPBoolean handled = kXMPFalse;

   if ((SHORT1FROMMP(event->mp1) & KC_VIRTUALKEY) &&
       (SHORT2FROMMP(event->mp2) == VK_DELETE)) 
   {
      handled = this->DoClear(focusFrame);
   } 
   else if (SHORT1FROMMP(event->mp2) == '/' &&
            SHORT1FROMMP(event->mp1) & KC_ALT)
   {
      handled = this->MoveToFront(focusFrame);
   }
   else if (SHORT1FROMMP(event->mp2) == '\\' &&
            SHORT1FROMMP(event->mp1) & KC_ALT)
   {
      handled = this->MoveToBack(focusFrame);
   }
   else if (SHORT1FROMMP(event->mp2) == '/' &&
            SHORT1FROMMP(event->mp1) & KC_CTRL)
   {
      handled = this->MoveForward(focusFrame);
   }
   else if (SHORT1FROMMP(event->mp2) == '\\' &&
            SHORT1FROMMP(event->mp1) & KC_CTRL)
   {
      handled = this->MoveBackward(focusFrame);
   }
   return handled;
}

// *** HandleMenuEvent ***

XMPBoolean ContainerPart::HandleMenuEvent(XMPFrame* focusFrame, XMPEventData event)
{
   ASSERTM(fMenuBar != kXMPNULL, 0, "Menubar is NULL");
   
   XMPBoolean handled = kXMPFalse;
   XMPCommandID command = LONGFROMMP(event->mp1);
   
   switch (command)
   {
      case cXMPGray:
      case cXMPRed:
      case cXMPGreen:
      case cXMPYellow:
      case cXMPBlue:
      case cXMPMagenta:
      case cXMPCyan:
      case cXMPWhite:
         {  //!!! This whole method do with some cleanup
            // Change SetBGColor to use a command number
            this->SetBGColor(focusFrame, command);
            handled = kXMPTrue;
         }
         break;
         
      case cXMPEmbedCntnr:
      case cXMPEmbedClock:
      case cXMPEmbedTest:
         {
           this->InvalidateSelection(focusFrame);
           this->EmptySelection();
           this->ClipEmbeddedFrames(focusFrame);
           this->Embed(command, focusFrame);
           handled = kXMPTrue;
         }
         break;
         
      case cXMPFreeze:
         {
           Proxy* p;
           OrderedCollectionIterator i(fSelection);
           for (p = (Proxy*) i.First(); i.IsNotComplete(); p = (Proxy*) i.Next())
           {  
              p->frame->SetFrozen(true);
           }
         }
         break;

      case cXMPDefrost:
         {
           Proxy* p;
           OrderedCollectionIterator i(fSelection);
           for (p = (Proxy*) i.First(); i.IsNotComplete(); p = (Proxy*) i.Next())
           {  
              p->frame->SetFrozen(false);
           }
         }
         break;
      case IDM_CLEAR     :
              this->DoClear(focusFrame);
              break;
      case cXMPMoveFront :
              this->MoveToFront(focusFrame);
              break;
      case cXMPMoveBack  :
              this->MoveToBack(focusFrame);
              break;
      case cXMPMoveFwd   :
              this->MoveForward(focusFrame);
              break;
      case cXMPMoveBkwd  :
              this->MoveBackward(focusFrame);
              break;

      default:
            if((command < (cXMPEmbedPart + cXMPMaxEmbedParts)) && (command >=  cXMPEmbedPart) )
               {
                this->InvalidateSelection(focusFrame);
                this->EmptySelection();
                this->ClipEmbeddedFrames(focusFrame);
                this->Embed(command, focusFrame);
                handled = kXMPTrue;
               }
         break;
   }
   return handled;
}

//------------------------------------------------------------------------------
// ContainerPart::DoCut
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::DoCut(XMPFrame* frame)
{
XMPUnused(frame);
   return kXMPFalse;
}

//------------------------------------------------------------------------------
// ContainerPart::DoCopy
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::DoCopy(XMPFrame* containingFrame)
{
XMPUnused(containingFrame);
   return kXMPFalse;
}

//------------------------------------------------------------------------------
// ContainerPart::DoPaste
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::DoPaste(XMPFrame* frame)
{
XMPUnused(frame);
   return kXMPFalse;
}

//------------------------------------------------------------------------------
// ContainerPart::DoPasteLink
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::DoPasteLink(XMPFrame* frame)
{
XMPUnused(frame);
   return kXMPFalse;
}

//------------------------------------------------------------------------------
// ContainerPart::DoClear
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::DoClear(XMPFrame* frame)
{
   Proxy* p;
   OrderedCollectionIterator i(fSelection);
   for (p = (Proxy*) i.First(); i.IsNotComplete(); p = (Proxy*) i.Next())
   {  
      p->frame->Invalidate(NULL);

      /* Make a copy of the frame's facet collection so that we can  */
      /* iterate through the copy.  The reason for this is that when */
      /* we call RemoveFacet, the facet is removed from the frame's  */
      /* collection of facet's thereby invalidating the iterator     */
      /* cursor.  By iterating through the copy collection, we avoid */
      /* this side effect.                                           */

      OrderedCollection* tempFacets = new OrderedCollection;
      
      XMPFrameFacetIterator* facets = p->frame->CreateFacetIterator();
      for (XMPFacet* f1 = facets->First();
            facets->IsNotComplete();
            f1 = facets->Next())
      {
         tempFacets->AddLast(f1);
      }
      delete facets;
      
      OrderedCollectionIterator t(tempFacets);
      for (XMPFacet* f2 = (XMPFacet*)t.First();
            t.IsNotComplete();
            f2 = (XMPFacet*)t.Next())
      {
         f2->GetContainingFacet()->RemoveFacet(f2);
         XMPDeleteObject(f2);
      }
      delete tempFacets;
      this->RemoveEmbeddedFrame(p->frame);
   }
   this->InvalidateSelection(frame);
   this->EmptySelection();
   this->ClipEmbeddedFrames(frame);
   this->GetStorageUnit()->GetDraft()->SetChangedFromPrev();
   return kXMPTrue;
}

//------------------------------------------------------------------------------
// ContainerPart::MoveToFront
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::MoveToFront(XMPFrame* frame)
{
   if (fSelection->Count() == 1) 
   {
      OrderedCollectionIterator i(fSelection);
      OrderedCollection *tempFacets = new OrderedCollection;
      Proxy* p = (Proxy*) i.First();

      fEmbeddedFrames->Remove(p->frame);
      fEmbeddedFrames->AddFirst(p->frame);
      fContents->Remove(p);
      fContents->AddFirst(p);
      XMPFrameFacetIterator* facets = p->frame->CreateFacetIterator();
      for (XMPFacet* facet = facets->First(); facets->IsNotComplete(); facet = facets->Next())
      {
         tempFacets->AddFirst(facet);
      }
      OrderedCollectionIterator iter(tempFacets);
      for (facet = (XMPFacet*)iter.First(); 
           iter.IsNotComplete(); 
           facet = (XMPFacet*)iter.Next()) 
      {
         facet->GetContainingFacet()->MoveBefore(facet, kXMPNULL);
      } 
      this->ClipEmbeddedFrames(frame);
      p->frame->Invalidate(kXMPNULL);
      this->GetStorageUnit()->GetDraft()->SetChangedFromPrev();
      
      delete tempFacets;
   }
   return kXMPTrue;
}

//------------------------------------------------------------------------------
// ContainerPart::MoveToBack
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::MoveToBack(XMPFrame* frame)
{
   if (fSelection->Count() == 1) 
   {
      OrderedCollectionIterator i(fSelection);
      OrderedCollection *tempFacets = new OrderedCollection;
      Proxy* p = (Proxy*) i.First();

      fEmbeddedFrames->Remove(p->frame);
      fEmbeddedFrames->AddLast(p->frame);
      fContents->Remove(p);
      fContents->AddLast(p);
      XMPFrameFacetIterator* facets = p->frame->CreateFacetIterator();
      for (XMPFacet* facet = facets->First(); facets->IsNotComplete(); facet = facets->Next())
      {
         tempFacets->AddLast(facet);
      }
      OrderedCollectionIterator iter(tempFacets);
      for (facet = (XMPFacet*)iter.First(); 
           iter.IsNotComplete(); 
           facet = (XMPFacet*)iter.Next()) 
      {
         facet->GetContainingFacet()->MoveBehind(facet, kXMPNULL);
      } 
      p->frame->Invalidate(kXMPNULL);
      this->ClipEmbeddedFrames(frame);
      this->GetStorageUnit()->GetDraft()->SetChangedFromPrev();
      
      delete tempFacets;
   }
   return kXMPTrue;
}

//------------------------------------------------------------------------------
// ContainerPart::MoveForward
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::MoveForward(XMPFrame* frame)
{
   if (fSelection->Count() == 1) 
   {
      OrderedCollectionIterator i(fSelection);
      OrderedCollection *tempFacets = new OrderedCollection;
      Proxy* p = (Proxy*) i.First();

      XMPFrame* siblingFrame = (XMPFrame*)fEmbeddedFrames->Before(p->frame);
      if (siblingFrame) 
      {
         fEmbeddedFrames->Remove(p->frame);
         fEmbeddedFrames->AddBefore(siblingFrame, p->frame);
         Proxy* siblingProxy = (Proxy*)fContents->Before(p);
         fContents->Remove(p);
         fContents->AddBefore(siblingProxy, p);
   
         XMPFrameFacetIterator* facets = p->frame->CreateFacetIterator();
         for (XMPFacet* facet = facets->First(); facets->IsNotComplete(); facet = facets->Next())
         {
            tempFacets->AddFirst(facet);
         }
         OrderedCollectionIterator iter(tempFacets);
         XMPFrameFacetIterator* siblingFacets = siblingFrame->CreateFacetIterator();
         XMPFacet* siblingFacet = siblingFacets->First();
   
         for (facet = (XMPFacet*)iter.First(); 
              iter.IsNotComplete(); 
              facet = (XMPFacet*)iter.Next()) 
         {
            facet->GetContainingFacet()->MoveBefore(facet, siblingFacet);
            siblingFacet = facet;
         } 
         this->ClipEmbeddedFrames(frame);
         p->frame->Invalidate(kXMPNULL);
         this->GetStorageUnit()->GetDraft()->SetChangedFromPrev();
      }
      delete tempFacets;
   }
   return kXMPTrue;
}

//------------------------------------------------------------------------------
// ContainerPart::MoveBackward
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::MoveBackward(XMPFrame* frame)
{
   if (fSelection->Count() == 1) 
   {
      OrderedCollectionIterator i(fSelection);
      OrderedCollection *tempFacets = new OrderedCollection;
      Proxy* p = (Proxy*) i.First();

      XMPFrame* siblingFrame = (XMPFrame*)fEmbeddedFrames->After(p->frame);
      if (siblingFrame)
      {
         fEmbeddedFrames->Remove(p->frame);
         fEmbeddedFrames->AddAfter( siblingFrame, p->frame);
         Proxy* siblingProxy = (Proxy*)fContents->After(p);
         fContents->Remove(p);
         fContents->AddAfter(siblingProxy, p);
        
         XMPFrameFacetIterator* facets = p->frame->CreateFacetIterator();
         for (XMPFacet* facet = facets->First(); facets->IsNotComplete(); facet = facets->Next())
         {
            tempFacets->AddLast(facet);
         }
        
         XMPFrameFacetIterator* siblingFacets = siblingFrame->CreateFacetIterator();
         XMPFacet* siblingFacet;
         for (facet = siblingFacets->First(); 
              siblingFacets->IsNotComplete(); 
              facet = siblingFacets->Next())
         {
            siblingFacet = facet;
         }
        
         OrderedCollectionIterator iter(tempFacets);
         for (facet = (XMPFacet*)iter.First(); 
              iter.IsNotComplete(); 
              facet = (XMPFacet*)iter.Next()) 
         {
            facet->GetContainingFacet()->MoveBehind(facet, siblingFacet);
            siblingFacet = facet;
         } 
         p->frame->Invalidate(kXMPNULL);
         this->ClipEmbeddedFrames(frame);
         this->GetStorageUnit()->GetDraft()->SetChangedFromPrev();
      }
      delete tempFacets;
   }
   return kXMPTrue;
}

//------------------------------------------------------------------------------
// ContainerPart::DoSelectAll
//------------------------------------------------------------------------------
   
XMPBoolean ContainerPart::DoSelectAll(XMPFrame* frame)
{
XMPUnused(frame);
   return kXMPFalse;
}


//------------------------------------------------------------------------------
// ContainerPart::AdjustMenus
//------------------------------------------------------------------------------
   
void ContainerPart::AdjustMenus(XMPFrame* frame)
{
   PartInfoRec* pInfo = (PartInfoRec*) frame->GetPartInfo();

   // if this frame is the root frame, enable printing.
   
   if (frame->GetContainingFrame() == kXMPNULL)
   {
      fMenuBar->EnableCommand(kXMPCommandPageSetup, kXMPTrue);
      fMenuBar->EnableCommand(kXMPCommandPrint, kXMPTrue);
   }
   else
   {
      fMenuBar->EnableCommand(kXMPCommandPageSetup, kXMPFalse);
      fMenuBar->EnableCommand(kXMPCommandPrint, kXMPFalse);
   }

   // if a part is selected 
      fMenuBar->EnableCommand(kXMPCommandGetPartInfo, kXMPTrue);
      fMenuBar->EnableCommand(kXMPCommandOpen, kXMPTrue);
      
   fMenuBar->EnableCommand(kXMPCommandViewAsWin, !frame->IsRoot());

   fMenuBar->EnableAndCheckCommand(cXMPGray, kXMPTrue, RGBEqual(pInfo->bgColor,rgbGray));
   fMenuBar->EnableAndCheckCommand(cXMPRed, kXMPTrue, RGBEqual(pInfo->bgColor,rgbRed));
   fMenuBar->EnableAndCheckCommand(cXMPGreen, kXMPTrue, RGBEqual(pInfo->bgColor,rgbGreen));
   fMenuBar->EnableAndCheckCommand(cXMPYellow, kXMPTrue, RGBEqual(pInfo->bgColor,rgbYellow));
   fMenuBar->EnableAndCheckCommand(cXMPBlue, kXMPTrue, RGBEqual(pInfo->bgColor,rgbBlue));
   fMenuBar->EnableAndCheckCommand(cXMPMagenta, kXMPTrue, RGBEqual(pInfo->bgColor,rgbMagenta));
   fMenuBar->EnableAndCheckCommand(cXMPCyan, kXMPTrue, RGBEqual(pInfo->bgColor,rgbCyan));
   fMenuBar->EnableAndCheckCommand(cXMPWhite, kXMPTrue, RGBEqual(pInfo->bgColor,rgbWhite));
   fMenuBar->EnableCommand(cXMPOtherColor, kXMPTrue);

   XMPBoolean hasSelection = fSelection->Count()>0;

   fMenuBar->EnableCommand(kXMPCommandCut, hasSelection);
   fMenuBar->EnableCommand(kXMPCommandCopy, hasSelection);
   fMenuBar->EnableCommand(kXMPCommandClear, hasSelection);

   fMenuBar->EnableCommand(kXMPCommandPaste, ValueOnClipboard(fSession));
   fMenuBar->EnableCommand(kXMPCommandPasteAs, kXMPFalse);
   fMenuBar->EnableCommand(kXMPCommandSelectAll, fEmbeddedFrames->Count()>0);
}

//-------------------------------------------------------------------------
// private utilities
//-------------------------------------------------------------------------

// *** ActivateFrame ***

void ContainerPart::ActivateFrame(XMPFrame* frame)
{
   if (frame != (fPalette ? fPalette->GetRootFrame() : kXMPNULL)) {
      PartInfoRec* pInfo = (PartInfoRec*) frame->GetPartInfo();
      if (!(pInfo->fIsActive))
      {
         XMPBoolean succeeded = false;
                        
         succeeded = fSession->GetArbitrator()->RequestFocusSet(fFocusSet,frame);
               
         if (succeeded)
         {
            this->FocusAcquired(fSelectionFocus, frame);
            this->FocusAcquired(fMenuFocus, frame);
            this->FocusAcquired(fKeyFocus, frame);
         }
      }
   }
}

// *** DeActivateFrame ***

void ContainerPart::DeActivateFrame(XMPFrame* frame)
{
   if (frame != (fPalette ? fPalette->GetRootFrame() : kXMPNULL)) {
      fSession->GetArbitrator()->RelinquishFocusSet(fFocusSet,frame);
      this->FocusLost(fSelectionFocus, frame);
      this->FocusLost(fMenuFocus, frame);
      this->FocusLost(fKeyFocus, frame);
   }
}

// *** ActivatingWindow ***

void ContainerPart::ActivatingWindow(XMPFrame* frame)
{
   PartInfoRec* pInfo = (PartInfoRec*) frame->GetPartInfo();
   if (pInfo->fNeedsActivating)
   {
      this->ActivateFrame(frame);
      this->ShowPalette();
      pInfo->fNeedsActivating = kXMPFalse;
   }
}

// *** DeActivatingWindow ***

void ContainerPart::DeActivatingWindow(XMPFrame* frame)
{
   PartInfoRec* pInfo = (PartInfoRec*) frame->GetPartInfo();
   if (frame == fSession->GetArbitrator()->GetFocusOwner(fSelectionFocus))
   {
      pInfo->fNeedsActivating = kXMPTrue;
      //this->DeActivateFrame(frame);
   }
   else
      pInfo->fNeedsActivating = kXMPFalse;
}


int ContainerPart::ExtractMenuPartStrings( char * fullstring
                                          , char * * dllstring
                                          , int * dllstringlength
                                          , char * * descripstring
                                          )
{
   char * tempstr = fullstring;
   while ((*tempstr) && (tempstr[0]==' ')) tempstr++;
   if (!*tempstr) return 1;  // skip this line. has no contents
   *dllstring = tempstr;
   tempstr = strchr(tempstr, ' ' );
   if (tempstr) {
      *dllstringlength = tempstr - *dllstring;
      while ((*tempstr) && (tempstr[0]==' ')) tempstr++;
      if (*tempstr) {
         *descripstring = tempstr;
      } else {
         *descripstring = *dllstring;
      } /* endif */
   } else {
      *descripstring = *dllstring;
      *dllstringlength = strlen(*dllstring);
   } /* endif */
   return 0;
}


//JYS: Create an accelerator table
static BOOL LoadAccelTable(HWND hwndFrame)
{
ULONG rc;
//char LoadError[100];
HMODULE hmodDLL;
HACCEL hAccel;

    rc = DosQueryModuleHandle("cntnrprt.dll", &hmodDLL);
    if ( rc == 0)
       hAccel = WinLoadAccelTable(1, hmodDLL, ID_ACCELTABLE);
    return WinSetAccelTable(1, hAccel, hwndFrame);
}

// *** InstallMenus ***

void ContainerPart::InstallMenus(XMPFrame* frame)
{
   //JYS: Cache hwndDesktopFrame
   if ( ! hwndDesktopFrame)
      hwndDesktopFrame = FrameToWindow( frame, fSession );
   if (frame && hwndDesktopFrame) {
      // Until the API is hashed out, just add the menu items you need.
      //   For anything you change here, you'll have to undo it in the
      //   RemoveMenus method too.  Hopefully the new design will
      //   simplify this. (jlc 94/4/15)
      {
         HWND hwndMenuBar = WinWindowFromID( hwndDesktopFrame, FID_MENU );
         // JYS: Begin submenu
         if ( ! ContainerPart::hwndEmbedSubMenu ) {

            MENUITEM mi;
            mi.afAttribute = 0;
            mi.afStyle = MIS_TEXT;
            mi.hwndSubMenu = 0;
            mi.hItem = 0;
             //Load accelerator table
            LoadAccelTable(hwndDesktopFrame);

            /* create Embed menu item and submenues */
            ContainerPart::hwndEmbedSubMenu = WinCreateWindow( HWND_DESKTOP,
                                           WC_MENU,
                                           "EmbedSubmenu",
                                           WS_VISIBLE,
                                           0,0, 0, 0,
                                           NULL,
                                           HWND_TOP,
                                           1710,
                                           NULL,
                                           NULL);

            FILE* partsFile = fopen("parts.dat", "r");
            char inbuf[60];

            for( int i = 0; i < cXMPMaxEmbedParts 
                            && fgets( inbuf, sizeof(inbuf), partsFile); i++) 
            {
               char *ptr;
               char *dllstring;
               char *descripstring;
               int  dllslen;
               if(ptr = strchr(inbuf, '\n')) *ptr = '\0';
               int rc = ExtractMenuPartStrings( inbuf
                                           , &dllstring
                                           , &dllslen
                                           , &descripstring
                                           );
               if (rc) {
                  i--;  // skip this one bogus entry
               } else {
                  dllstring[dllslen] = 0;
                  strcpy( partStrings[i], dllstring);
                  mi.iPosition = /*2 +*/ i;     
                  mi.id = cXMPEmbedPart + i;    
                  WinSendMsg(ContainerPart::hwndEmbedSubMenu, MM_INSERTITEM, &mi, 
                               (MPARAM)descripstring);
               } /* endif */
            } /* endif */
            fclose(partsFile);
            /* Create color menu item and submenus */
            ContainerPart::hwndColorSubMenu = WinCreateWindow( HWND_DESKTOP,
                                           WC_MENU,
                                           "ColorSubmenu",
                                           WS_VISIBLE,
                                           0,0, 0, 0,
                                           NULL,
                                           HWND_TOP,
                                           1710,
                                           NULL,
                                           NULL);

            int id;
            for (i=0, id = cXMPGray; i < 8; i++, id++) {
               mi.iPosition = 0;
               mi.id = id;
               WinSendMsg(ContainerPart::hwndColorSubMenu, MM_INSERTITEM, &mi, (MPARAM)apszColors[i]);
            }

            /* Create Arrange menu item and submenu*/
            ContainerPart::hwndArrangeSubMenu = WinCreateWindow( HWND_DESKTOP,
                                                  WC_MENU,
                                                  "ArrangeSubmenu",
                                                  WS_VISIBLE,
                                                  0,0,0,0,
                                                  NULL,
                                                  HWND_TOP,
                                                  1710,
                                                  NULL,
                                                  NULL);
            mi.iPosition = 0;
            mi.id = cXMPMoveBkwd;
            WinSendMsg(ContainerPart::hwndArrangeSubMenu, MM_INSERTITEM, &mi, (MPARAM)"Move Backward\tAlt+Down");
            mi.iPosition = 0;
            mi.id = cXMPMoveFwd;
            WinSendMsg(ContainerPart::hwndArrangeSubMenu, MM_INSERTITEM, &mi, (MPARAM)"Move Forward\tAlt+Up");
            mi.iPosition = 0;
            mi.id = cXMPMoveBack;
            WinSendMsg(ContainerPart::hwndArrangeSubMenu, MM_INSERTITEM, &mi, (MPARAM)"Move To Back\tShift+Down");
            mi.iPosition = 0;
            mi.id = cXMPMoveFront;
            WinSendMsg(ContainerPart::hwndArrangeSubMenu, MM_INSERTITEM, &mi, (MPARAM)"Move To Front\tShift+Up");

            /* Create Frame menu item and submenus */
            ContainerPart::hwndFrameSubMenu = WinCreateWindow( HWND_DESKTOP,
                                           WC_MENU,
                                           "FrameSubmenu",
                                           WS_VISIBLE,
                                           0,0, 0, 0,
                                           NULL,
                                           HWND_TOP,
                                           1710,
                                           NULL,
                                           NULL);
            mi.iPosition = 0;
            mi.id = cXMPDefrost;
            WinSendMsg(ContainerPart::hwndFrameSubMenu, MM_INSERTITEM, &mi, (MPARAM)"Defrost");

            mi.iPosition = 0;
            mi.id = cXMPFreeze;
            WinSendMsg(ContainerPart::hwndFrameSubMenu, MM_INSERTITEM, &mi, (MPARAM)"Freeze");
         } /* endif */ // Submenu creation

         MENUITEM mbar;
         mbar.afStyle = MIS_SUBMENU;
         mbar.afAttribute = 0;
         mbar.hItem = 0;

         //WinShowWindow(hwndMenuBar, FALSE);
         // See note below other use of WSW.
         mbar.iPosition = 2;
         mbar.id = cXMPColor;
         mbar.hwndSubMenu = ContainerPart::hwndColorSubMenu;  // no submenu for this one
         WinSendMsg(hwndMenuBar, MM_INSERTITEM, &mbar, (MPARAM)"Background");


         mbar.iPosition = 3;
         mbar.id = cXMPEmbed;
         mbar.hwndSubMenu = ContainerPart::hwndEmbedSubMenu;  // no submenu for this one
         WinSendMsg(hwndMenuBar, MM_INSERTITEM, &mbar, (MPARAM)"Embed");

         HWND hwndEdit;
         MENUITEM itemEdit;
         ULONG idEdit =(ULONG) WinSendMsg(hwndMenuBar, MM_ITEMIDFROMPOSITION, MPFROM2SHORT(1, 0), (MPARAM)NULL);
         WinSendMsg(hwndMenuBar, MM_QUERYITEM, MPFROM2SHORT((SHORT)idEdit,TRUE),(MPARAM)&itemEdit);
         hwndEdit = itemEdit.hwndSubMenu;

         mbar.afStyle = MIS_TEXT;
         mbar.iPosition = MIT_END;
         mbar.hwndSubMenu = 0;
         mbar.id = cXMPClear;
         WinSendMsg(hwndEdit, MM_INSERTITEM, &mbar, (MPARAM)"Clear\tDel");

         mbar.afStyle = MIS_SUBMENU;
         mbar.iPosition = MIT_END;
         mbar.id = cXMPArrange;
         mbar.hwndSubMenu = ContainerPart::hwndArrangeSubMenu;  // no submenu for this one
         WinSendMsg(hwndEdit, MM_INSERTITEM, &mbar, (MPARAM)"Arrange");

         mbar.iPosition = MIT_END;
         mbar.id = cXMPFrame;
         mbar.hwndSubMenu = ContainerPart::hwndFrameSubMenu;  // no submenu for this one
         WinSendMsg(hwndEdit, MM_INSERTITEM, &mbar, (MPARAM)"Frame");
         WinInvalidateRect(hwndMenuBar, NULL, TRUE);
      }

   } /* endif */
}

// *** ValueOnClipboard ***

XMPBoolean ValueOnClipboard(XMPSession* session)
{
   XMPBoolean     result = kXMPFalse;
   XMPClipboard*  clipboard = session->GetClipboard();
   XMPClipboardKey key;

   if (clipboard->Lock(0, &key))
   {
      TRY
         XMPStorageUnit* su = clipboard->GetContentStorageUnit(key);
         result =  su->Exists(kXMPPropContents, kXMPNULL, 0);
      CATCH_ALL
      ENDTRY
      clipboard->Unlock(key);
   }
   return result;
}

// *** RemoveMenus ***
void ContainerPart::RemoveMenus(XMPFrame* frame)
{
   // Until the API is hashed out, everything you add/remove
   //   needs to be restored.
   {
      HWND hwndDesktopFrame = FrameToWindow( frame, fSession );
      if (!hwndDesktopFrame) {
         // internal logic error
         WinMBox("RemoveMenus doesn't know the window handle.");
         return;
      } /* endif */
      HWND hwndMenuBar = WinWindowFromID( hwndDesktopFrame, FID_MENU );
      int idx;
         HWND hwndEdit;
         MENUITEM itemEdit;
  ULONG idEdit =(ULONG) WinSendMsg(hwndMenuBar, MM_ITEMIDFROMPOSITION, MPFROM2SHORT(1, 0), (MPARAM)NULL);

         WinSendMsg(hwndMenuBar, MM_QUERYITEM, MPFROM2SHORT((SHORT)idEdit,TRUE),(MPARAM)&itemEdit);
         hwndEdit = itemEdit.hwndSubMenu;


      idx = (int) WinSendMsg( hwndEdit, MM_REMOVEITEM, MPFROM2SHORT(cXMPClear, TRUE), (MPARAM)NULL);
      idx = (int) WinSendMsg( hwndEdit, MM_REMOVEITEM, MPFROM2SHORT(cXMPFrame, TRUE), (MPARAM)NULL);
      idx = (int) WinSendMsg( hwndMenuBar, MM_REMOVEITEM, MPFROM2SHORT(cXMPColor, TRUE), (MPARAM)NULL);
      idx = (int) WinSendMsg( hwndEdit, MM_REMOVEITEM, MPFROM2SHORT(cXMPArrange, TRUE), (MPARAM) NULL);
      //WinShowWindow(hwndMenuBar, TRUE);
      // We are calling WinShowWindow here to reduce flashing and
      //   hopefully to speed up the changing of the menus.  One
      //   would think that the WinShowWindow should belong below
      //   the final WinSendMsg, but that seems to give odd results.
      //   Try it for yourself.  I haven't tried WinLockWindowUpdate
      //   or WinEnableWindowUpdate.
      idx = (int) WinSendMsg( hwndMenuBar, MM_REMOVEITEM, MPFROM2SHORT(cXMPEmbed, TRUE), (MPARAM)NULL);

      WinInvalidateRect(hwndMenuBar, NULL, TRUE);
   }
}


// *** SetBGColor ***

void ContainerPart::SetBGColor(XMPFrame* frame, XMPCommandID whichColor)
{
   PartInfoRec* pInfo = (PartInfoRec *) frame->GetPartInfo();
   if (!pInfo)
   {  pInfo = new PartInfoRec;
      frame->SetPartInfo((XMPInfoType) pInfo);
   };

   RGBColor newColor = pInfo->bgColor;
   
   switch (whichColor)
   {
      break;
      case  cXMPGray:    newColor = rgbGray;      break;
      case  cXMPRed:     newColor = rgbRed;       break;
      case  cXMPGreen:   newColor = rgbGreen;     break;
      case  cXMPYellow:  newColor = rgbYellow;    break;
      case  cXMPBlue:    newColor = rgbBlue;      break;
      case  cXMPMagenta: newColor = rgbMagenta;   break;
      case  cXMPCyan:    newColor = rgbCyan;      break;
      case  cXMPWhite:   newColor = rgbWhite;     break;
      default: break;
   }
   UserSetBGColor(frame,newColor);
}

struct SetBGColorRec
{
   SetBGColorRec(RGBColor oldColor, RGBColor newColor, XMPFrame* frame)
               {fOldColor = oldColor; fNewColor = newColor;
                  fFrame = frame;}
   RGBColor    fOldColor;
   RGBColor    fNewColor;
   XMPFrame*   fFrame;
};

// *** UserSetBGColor ***

void ContainerPart::UserSetBGColor(XMPFrame* frame, RGBColor whichColor)
{
   PartInfoRec* pInfo = (PartInfoRec *) frame->GetPartInfo();
   if (!pInfo)
   {  pInfo = new PartInfoRec;
      frame->SetPartInfo((XMPInfoType) pInfo);
   }
   if (!RGBEqual(whichColor,pInfo->bgColor))
   {
      this->SetBGColor(frame, whichColor);
   }
}

// *** SetBGColor ***

void ContainerPart::SetBGColor(XMPFrame* frame, RGBColor whichColor)
{
   PartInfoRec* pInfo = (PartInfoRec *) frame->GetPartInfo();
   if (!pInfo)
   {  pInfo = new PartInfoRec;
      frame->SetPartInfo((XMPInfoType) pInfo);
   }
   if (!RGBEqual(whichColor,pInfo->bgColor))
   {
      pInfo->bgColor = whichColor;
      this->GetStorageUnit()->GetDraft()->SetChangedFromPrev();
      frame->Invalidate(NULL);
   }
}

void ContainerPart::GetBGColor(XMPUShort whichColor, RGBColor* newColor)
{
   switch (whichColor)
   {
      case  kGray: *newColor = rgbGray;      break;
      case  kRed: *newColor = rgbRed;     break;
      case  kGreen: *newColor = rgbGreen;    break;
      case  kYellow: *newColor = rgbYellow;  break;
      case  kBlue: *newColor = rgbBlue;      break;
      case  kMagenta: *newColor = rgbMagenta;   break;
      case  kCyan: *newColor = rgbCyan;      break;
      case  kWhite: *newColor = rgbWhite;    break;
      
      default:
         WASSERTM(kXMPFalse, "No such color.");
         break;
   }
}

void ContainerPart::SetNewBounds(XMPFrame* containedFrame, Rect* r)
{

   Proxy* p = ProxyForFrame(containedFrame);
   XMPTransform* externalXfrm = p->transform;
   XMPShape* shape = containedFrame->GetFrameShape();
   
   XMPTransform* newXForm = new XMPTransform;
   newXForm->CopyFrom(externalXfrm);
   Point pt = *(Point*)r;
   XMPPoint xmpPt = pt;
   newXForm->MoveBy(xmpPt);            // I don't want to move it by,
                                 // I want to set it to something!
   p->transform = newXForm;         // is this right?  how else will it
                              // know?

   XMPShape* newShape = new XMPShape;
   XMPRect xmpr = *r;
   newShape->SetRectangle(&xmpr);
   
   XMPFrameFacetIterator* fi = containedFrame->CreateFacetIterator();
   for(XMPFacet* facet = fi->First(); fi->IsNotComplete();
         facet = fi->Next())
   {
      facet->ChangeExternalTransform(newXForm);
      facet->Invalidate(facet->GetClipShape());
   }
   containedFrame->ChangeFrameShape(newShape);

}  // SetNewBounds()


// *** Embed ***
void ContainerPart::Embed(long item, XMPFrame* frame, XMPPart** newPart)
{
   XMPPart* embeddedPart;
   XMPFrame* embeddedFrame;
   XMPTransform* externalXForm = new XMPTransform;
   XMPSLong count = 1 + fContents->Count();

   XMPStorageUnit* su = this->GetStorageUnit();
   
   switch (item)
   {
      #if 0
      case cXMPEmbedCntnr:
         embeddedPart = su->GetDraft()->CreatePart(
                                       "CntnrPrt" /* kXMPPartKeyPart */,
                                       kXMPNULL);
         break;
      case cXMPEmbedClock:
         embeddedPart = su->GetDraft()->CreatePart(
                                       "ClockPrt" /* kXMPPartClockPart */,
                                       kXMPNULL);
         break;
      #endif
      default: 
         embeddedPart = su->GetDraft()->CreatePart(partStrings[item - cXMPEmbedPart],kXMPNULL);  
            if(!embeddedPart)
            {
               // print error message and return if unable to load dll
               //char errmsg[100];
               //sprintf(errmsg, "Unable to load module for %s, verify DLL exists and retry!", 
               //         partStrings[item - cXMPEmbedPart]);
               //WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, errmsg, "Error Information", 0, MB_ERROR | MB_OK);
               delete externalXForm;
               return;
            }
         break;
   }
   Point ptTemp = {(count*20),(count*20)};
   externalXForm->MoveBy(ptTemp);
   embeddedFrame = this->CreateEmbeddedFrame(frame,
                                    NULL,
                                    externalXForm,
                                    embeddedPart,
                                    kXMPNullTypeToken,
                                    kXMPNullTypeToken,
                                    0,
                                    kXMPFalse);
                                    
   XMPFrameFacetIterator* facets = frame->CreateFacetIterator();
   for (XMPFacet* facet = facets->First(); facets->IsNotComplete(); facet = facets->Next())
   {
      XMPShape* clip = new XMPShape;
      clip->CopyFrom(embeddedFrame->GetFrameShape());
      XMPTransform* xform = new XMPTransform;
      xform->CopyFrom(this->ProxyForFrame(embeddedFrame)->transform);
      facet->CreateEmbeddedFacet(embeddedFrame, clip, xform, kXMPNULL, kXMPFrameInFront);
   }
   delete facets;
   this->ClipEmbeddedFrames(frame);
   frame->Invalidate(NULL);
   
   if (newPart)
      *newPart = embeddedPart;
   else
      XMPReleaseObject(embeddedPart);
}

void ContainerPart::UpdateProxyRegion(Proxy* proxy)
{
   XMPShape* scratch = new XMPShape();
   scratch->CopyFrom(proxy->frame->GetFrameShape());
   scratch->Transform(proxy->transform);
   RgnHandle scratchRgn = scratch->GetQDRegion();
   GpiCombineRegion(hpsMem, proxy->region, scratchRgn, 0, CRGN_COPY );
   delete scratch;
}

//------------------------------------------------------------------------------
// Frame Shape Transformation Utilities
//------------------------------------------------------------------------------



// *** SetGrafPortOrigin ***

void ContainerPart::SetGrafPortOrigin(XMPFacet* facet)
{
   XMPTransform* localToGlobal = facet->GetContentTransform();
   HPS hps = facet->GetCanvas()->GetPlatformCanvas();
   PMATRIXLF mtx = (PMATRIXLF)facet->GetContentTransform()->GetPlatformTransform();

   // This is a hack until GetPlatformTransform() is fixed to return a 
   // valid MATRIXLF transform (i.e. uses FIXED values where required)
   MATRIXLF mtxTemp = *mtx;
   mtxTemp.fxM11 *= 0x10000;
   mtxTemp.fxM12 *= 0x10000;
   mtxTemp.fxM21 *= 0x10000;
   mtxTemp.fxM22 *= 0x10000;

   GpiSetModelTransformMatrix(hps, 9, &mtxTemp, TRANSFORM_REPLACE);
}

//------------------------------------------------------------------------------
// ContainerPart::HasExtension
//------------------------------------------------------------------------------

// *** HasExtension ***

XMPBoolean  ContainerPart::HasExtension(XMPType extensionName)
{
   XMPISOStr   semtIntf = "SemanticInterface";
   if (!XMPISOStrCompare((XMPISOStr)extensionName, semtIntf))
      return kXMPTrue;
   else
      return kXMPFalse;
}

   
//------------------------------------------------------------------------------
// ContainerPart::GetExtension
//------------------------------------------------------------------------------


// *** GetExtension ***

XMPExtension*  ContainerPart::GetExtension(XMPType extensionName)
{
   XMPISOStr   semtIntf = "SemanticInterface";
   if (!XMPISOStrCompare((XMPISOStr)extensionName, semtIntf))
      return fSemtIntf;
   else
      return kXMPNULL;
}

//==============================================================================
// ContainerPartEmbeddedFramesIterator
//==============================================================================

XMPFrame*   ContainerPartEmbeddedFramesIterator::First()
{
   return (XMPFrame*) fOCIter->First();
}

XMPFrame*   ContainerPartEmbeddedFramesIterator::Next()
{
   return (XMPFrame*) fOCIter->Next();
}

XMPBoolean  ContainerPartEmbeddedFramesIterator::IsNotComplete()
{
   return fOCIter->IsNotComplete();
}

ContainerPartEmbeddedFramesIterator::ContainerPartEmbeddedFramesIterator(XMPPart* part)
{
   // !!! fix this - no allocation in constructor!
   fOCIter = ((ContainerPart*) part)->GetEmbeddedFrames()->CreateIterator();
}

ContainerPartEmbeddedFramesIterator::~ContainerPartEmbeddedFramesIterator()
{
   delete fOCIter;
}

void ContainerPart::CreatePalette()
{
}

void ContainerPart::DestroyPalette()
{
   if (fPalette != kXMPNULL) {
      fPalette->CloseAndRemove();
      fPalette = kXMPNULL;
   }
}

void ContainerPart::ShowPalette()
{
   if (fPaletteUp == kXMPTrue) {
      if (fPalette == kXMPNULL)
         this->CreatePalette();
      fPalette->Show();
   }
}

void ContainerPart::HidePalette()
{
   if (fPalette != kXMPNULL) {
      fPalette->Hide();
   }
}

void ContainerPart::UndoAction(XMPActionData actionState)
{
   // FOR NOW, WE JUST HAVE ONE ACTION THAT CAN BE UNDONE OR REDONE.
   SetBGColorRec* undoRec = (SetBGColorRec*)actionState;
   
   this->SetBGColor(undoRec->fFrame, undoRec->fOldColor);
}

void ContainerPart::RedoAction(XMPActionData actionState)
{
   // FOR NOW, WE JUST HAVE ONE ACTION THAT CAN BE UNDONE OR REDONE.
   SetBGColorRec* undoRec = (SetBGColorRec*)actionState;
   
   this->SetBGColor(undoRec->fFrame, undoRec->fNewColor);
}

void ContainerPart::DisposeActionState(XMPActionData actionState,
                              XMPDoneState doneState)
{
   XMPUnused(doneState);
   // FOR NOW, WE JUST HAVE ONE ACTION THAT CAN BE UNDONE OR REDONE.
   delete (SetBGColorRec*)actionState;
}

