//----------------------------------------------------------------------------
// ObjectWindows
// (C) Copyright 1994 by Borland International, All Rights Reserved
//
//   Implementation of TOleMetaPict, manipulates Ole2 MetafilePict/Icon
//----------------------------------------------------------------------------
#define INC_OLE2
#include <owl/owlpch.h>
#include <owl/oledlg.h>

//
// OWL OLE Dialog diagnostic group.
//
DIAG_DECLARE_GROUP(OwlOleDlg);

//
// Class used for label and source extraction from a metafile
//
class _OWLCLASS TLabelExtract {
  public:
    //
    // Use memset to initialize PODS
    //
    TLabelExtract() {memset(this, 0, sizeof(TLabelExtract));}

    LPSTR       lpsz;
    UINT        Index;      // index in lpsz (so we can retrieve 2+ lines)
    DWORD       PrevIndex;  // index of last line (so we can mimic word wrap)

    union {
      UINT      cch;        //Length of label for label extraction
      UINT      iIcon;      //Index of icon in source extraction.
    } u;

    //
    //For internal use in enum procs
    //
    BOOL        fFoundIconOnly;
    BOOL        fFoundSource;
    BOOL        fFoundIndex;
};

//
// Class used for extracting icons from a metafile (CreateIcon parameters)
//
class _OWLCLASS TIconExtract {
  public:
    //
    // Use memset to initialize PODS
    //
    TIconExtract() {memset(this, 0, sizeof(TIconExtract));}

    HICON       hIcon;          // Icon created in the enumeration proc.
    BOOL        fAND;
    HGLOBAL     hMemAND;        // Enumeration proc allocates and copies
    HINSTANCE   hInst;          // Module instance handle
};

//
// Class used to pass info to EnumMetafileDraw
//
class _OWLCLASS TDrawInfo {
  public:
    //
    // Use memset to initialize PODS
    //
    TDrawInfo() {memset(this, 0, sizeof(TDrawInfo));}

    RECT     Rect;
    BOOL     fIconOnly;
};

//
// String [comment] in Metafile which flags that the label follows
//
static char szIconOnly[] = "IconOnly";       

int CALLBACK __export EnumMetafileIconDraw(HDC, HANDLETABLE FAR*, METARECORD FAR*, int, LPARAM);
int CALLBACK __export EnumMetafileExtractLabel(HDC, HANDLETABLE FAR*, METARECORD FAR*, int, TLabelExtract far*);
int CALLBACK __export EnumMetafileExtractIcon(HDC, HANDLETABLE FAR*, METARECORD FAR*, int, TIconExtract far*);
int CALLBACK __export EnumMetafileExtractIconSource(HDC, HANDLETABLE FAR*, METARECORD FAR*, int, TLabelExtract far*);

//
//
//
static uint
MapLogHimToPix(int hiMetric, int pelsPerInch)
{
  return (uint)(((double)hiMetric * pelsPerInch)/HiMetricPerInch);
}

//
//
//
static int
XformWidthInHimetricToPixels(TDC& dc, int widthInHiMetric)
{
  int logPix   = dc.GetDeviceCaps(LOGPIXELSX);
  int pixWidth = MapLogHimToPix(widthInHiMetric, logPix);
  return pixWidth;
}

//
//
//
static int
XformHeightInHimetricToPixels(TDC& dc, int heightInHiMetric)
{
  int logPix    = dc.GetDeviceCaps(LOGPIXELSY);
  int pixHeight = MapLogHimToPix(heightInHiMetric, logPix);
  return pixHeight;
}

//
//
//
BOOL 
OleUIMetafilePictIconDraw(HDC hDC, LPRECT pRect, 
                          HGLOBAL hMetaPict, BOOL fIconOnly)
{
  LPMETAFILEPICT  pMF;
  TDrawInfo        di;
  int             cx, cy;
  SIZE            size;
  POINT           point;

  if (!hMetaPict)
    return FALSE;

  pMF = (LPMETAFILEPICT)GlobalLock(hMetaPict);

  if (NULL==pMF) {
    TRACEX(OwlOleDlg, 1, "Unable to lock MetaPict Handle");
    return FALSE;
  }

  di.Rect = *pRect;
  di.fIconOnly = fIconOnly;

  //
  // Transform to back to pixels
  //
  cx = XformWidthInHimetricToPixels(TDC(hDC), pMF->xExt);
  cy = XformHeightInHimetricToPixels(TDC(hDC), pMF->yExt);

  SaveDC(hDC);

  SetMapMode(hDC, pMF->mm);
  SetViewportOrgEx(hDC, (pRect->right - cx) / 2, 0, &point);

  SetViewportExtEx(hDC, min ((pRect->right - cx) / 2 + cx, cx), cy, &size);

  if (fIconOnly) {
    EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileIconDraw, 
                (LPARAM)(TDrawInfo far*)&di);
  }
  else {
    PlayMetaFile(hDC, pMF->hMF);
  }

  RestoreDC(hDC, -1);
  GlobalUnlock(hMetaPict);
  return TRUE;
}

int CALLBACK __export
EnumMetafileIconDraw(HDC hDC, HANDLETABLE FAR *phTable, 
                     METARECORD FAR *pMFR, 
                     int cObj, LPARAM lParam)
{
  TDrawInfo far* lpdi = (TDrawInfo far*)lParam;

  //
  // We play everything blindly except for DIBBITBLT (or DIBSTRETCHBLT)
  // and ESCAPE with MFCOMMENT.  For the BitBlts we change the x,y to
  // draw at (0,0) instead of wherever it was written to draw.  The
  // comment tells us there to stop if we don't want to draw the label.
  //

  //
  //If we're playing icon only, stop enumeration at the comment.
  //
  if (lpdi->fIconOnly) {
    if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0]) {
      if (0==lstrcmpi(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
        return 0;
    }

    //
    // Check for the records in which we want to munge the coordinates.
    // destX is offset 6 for BitBlt, offset 9 for StretchBlt, either of
    // which may appear in the metafile.
    // 
    if (META_DIBBITBLT==pMFR->rdFunction)
      pMFR->rdParm[6]=0;

    if (META_DIBSTRETCHBLT==pMFR->rdFunction)
        pMFR->rdParm[9] = 0;
  }

  PlayMetaFileRecord(hDC, phTable, pMFR, cObj);
  return 1;
}

UINT
OleUIMetafilePictExtractLabel(HGLOBAL hMetaPict, LPSTR lpszLabel,
                              UINT cchLabel, LPDWORD lpWrapIndex)
{
  LPMETAFILEPICT  pMF;
  TLabelExtract    le;
  HDC             hDC;

  //
  // We extract the label by getting a screen DC and walking the metafile
  // records until we see the ExtTextOut record we put there.  That
  // record will have the string embedded in it which we then copy out.
  //
  if (!hMetaPict || !lpszLabel || !cchLabel)
    return FALSE;

  pMF= (LPMETAFILEPICT)GlobalLock(hMetaPict);

  if (NULL==pMF)
    return FALSE;

  le.lpsz=lpszLabel;
  le.u.cch=cchLabel;
  le.Index=0;
  le.fFoundIconOnly=FALSE;
  le.fFoundSource=FALSE;
  le.fFoundIndex=FALSE;
  le.PrevIndex = 0;

  //
  // Use a screen DC so we have something valid to pass in.
  //
  hDC=GetDC(NULL);
  EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractLabel,
              (LONG)(TLabelExtract far*)&le);
  ReleaseDC(NULL, hDC);

  GlobalUnlock(hMetaPict);

  //
  // Tell where we wrapped (if calling function cares)
  //
  if (lpWrapIndex)
    *lpWrapIndex = le.PrevIndex;

  //
  // Return amount of text copied
  //
  return le.u.cch;
}

int CALLBACK __export
EnumMetafileExtractLabel(HDC /*hDC*/,
                         HANDLETABLE FAR* /*phTable*/,
                         METARECORD FAR *pMFR,
                         int /*cObj*/, 
                         TLabelExtract far* pLE)
{
  //
  // We don't allow anything to happen until we see "IconOnly"
  // in an MFCOMMENT that is used to enable everything else.
  // 
  if (!pLE->fFoundIconOnly) {
    if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0]) {
      if (0==lstrcmpi(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
        pLE->fFoundIconOnly=TRUE;
    }

    return 1;
  }

  //
  // Enumerate all records looking for META_EXTTEXTOUT - there can be more
  // than one.
  //
  if (META_EXTTEXTOUT==pMFR->rdFunction) {
    UINT        cchMax;
    LPSTR       lpszTemp;

    //
    // If ExtTextOut has NULL fuOptions, then the rectangle is omitted
    // from the record, and the string starts at rdParm[4].  If
    // fuOptions is non-NULL, then the string starts at rdParm[8]
    // (since the rectange takes up four WORDs in the array).  In
    // both cases, the string continues for (rdParm[2]+1) >> 1
    // words.  We just cast a pointer to rdParm[8] to an LPSTR and
    // lstrcpyn into the buffer we were given.
    //
    // Note that we use element 8 in rdParm instead of 4 because we
    // passed ETO_CLIPPED in for the options on ExtTextOut--docs say
    // [4] which is rect doesn't exist if we passed zero there.
    //
    // 
    cchMax=min(pLE->u.cch - pLE->Index, (UINT)pMFR->rdParm[2]);
    lpszTemp = pLE->lpsz + pLE->Index;

    lstrcpyn(lpszTemp, (LPSTR)&(pMFR->rdParm[8]), cchMax + 1);

    pLE->PrevIndex = pLE->Index;
    pLE->Index += cchMax;
  }

  return 1;
}

HICON 
OleUIMetafilePictExtractIcon(HGLOBAL hMetaPict, HINSTANCE hinst)
{
  LPMETAFILEPICT  pMF;
  HDC             hDC;
  TIconExtract     ie;

  //
  // We extract the label by getting a screen DC and walking the metafile
  // records until we see the ExtTextOut record we put there.  That
  // record will have the string embedded in it which we then copy out.
  // 

  if (NULL==hMetaPict)
    return NULL;

  pMF= (LPMETAFILEPICT)GlobalLock(hMetaPict);

  if (NULL==pMF)
    return FALSE;

  ie.fAND=TRUE;
  ie.hInst = hinst;

  hDC=GetDC(NULL);
  EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractIcon, 
              (LONG)(TIconExtract far*)&ie);
  ReleaseDC(NULL, hDC);
  GlobalUnlock(hMetaPict);

  return ie.hIcon;
}

int CALLBACK __export
EnumMetafileExtractIcon(HDC hDC, HANDLETABLE FAR* /*phTable*/,
                        METARECORD FAR *pMFR, int /*cObj*/,
                        TIconExtract far* pIE)
{
  LPBITMAPINFO        lpBI;
  LPBITMAPINFOHEADER  lpBH;
  LPBYTE              lpbSrc;
  LPBYTE              lpbDst;
  UINT                uWidth, uHeight;
  DWORD               cb;
  HGLOBAL             hMem;
  BITMAP              bm;
  HBITMAP             hBmp;

  //
  // Continue enumeration if we don't see the records we want.
  //
  if (META_DIBBITBLT != pMFR->rdFunction && 
      META_DIBSTRETCHBLT != pMFR->rdFunction)
    return 1;

  //
  // Windows 3.0 DrawIcon uses META_DIBBITBLT in whereas 3.1 uses
  // META_DIBSTRETCHBLT so we have to handle each case separately.
  // 

  if (META_DIBBITBLT==pMFR->rdFunction) {
    //
    // Get dimensions and the BITMAPINFO struct.
    //
    uHeight=pMFR->rdParm[1];
    uWidth =pMFR->rdParm[2];
    lpBI=(LPBITMAPINFO)&(pMFR->rdParm[8]);
  }
  if (META_DIBSTRETCHBLT==pMFR->rdFunction) {
    //
    // Get dimensions and the BITMAPINFO struct.
    //
    uHeight=pMFR->rdParm[2];
    uWidth =pMFR->rdParm[3];
    lpBI=(LPBITMAPINFO)&(pMFR->rdParm[10]);
  }

  lpBH=(LPBITMAPINFOHEADER)&(lpBI->bmiHeader);

  //
  // Pointer to the bits which follows the BITMAPINFO structure.
  //
  lpbSrc=(LPBYTE)lpBI+sizeof(BITMAPINFOHEADER);

  //
  // Add the length of the color table.
  //
  if (0!=lpBH->biClrUsed)
    lpbSrc+=(int)(DWORD)(lpBH->biClrUsed*sizeof(RGBQUAD));
  else  {
    //
    // 1 << bc gives 2, 16, 256 for 1, 4, or 256 colors
    lpbSrc+=(int)(DWORD)((1 << (lpBH->biBitCount))*sizeof(RGBQUAD));
  }

  //
  // All the bits we have in lpbSrc are device-independent, so we
  // need to change them over to be device-dependent using SetDIBits.
  // Once we have a bitmap with the device-dependent bits, we can
  // GetBitmapBits to have buffers with the real data.
  //
  // For each pass we have to allocate memory for the bits.  We save
  // the memory for the mask between passes.
  // 
     
  //
  // Use CreateBitmap for ANY monochrome bitmaps
  //
  if (pIE->fAND || 1==lpBH->biBitCount || lpBH->biBitCount > 8)
    hBmp=CreateBitmap((UINT)lpBH->biWidth, (UINT)lpBH->biHeight, 1, 1, NULL);
  else if (lpBH->biBitCount <= 8)
    hBmp=CreateCompatibleBitmap(hDC, (UINT)lpBH->biWidth, (UINT)lpBH->biHeight);

  if (!hBmp || !SetDIBits(hDC, hBmp, 0, (UINT)lpBH->biHeight, 
                         (LPVOID)lpbSrc, lpBI, DIB_RGB_COLORS)) {
    if (!pIE->fAND)
      GlobalFree(pIE->hMemAND);

    DeleteObject(hBmp);
    return 0;
  }

  //
  // Allocate memory and get the DDBits into it.
  //
  GetObject(hBmp, sizeof(bm), &bm);

  cb=bm.bmHeight*bm.bmWidthBytes * bm.bmPlanes;

  hMem=GlobalAlloc(GHND, cb);

  if (NULL==hMem) {
    if (NULL!=pIE->hMemAND)
      GlobalFree(pIE->hMemAND);

    DeleteObject(hBmp);
    return 0;
  }

  lpbDst=(LPBYTE)GlobalLock(hMem);
  GetBitmapBits(hBmp, cb, (LPVOID)lpbDst);

  DeleteObject(hBmp);
  GlobalUnlock(hMem);

  //
  // If this is the first pass (pIE->fAND==TRUE) then save the memory
  // of the AND bits for the next pass.
  // 
  if (pIE->fAND) {
    pIE->fAND=FALSE;
    pIE->hMemAND=hMem;

    //
    // Continue enumeration looking for the next blt record.
    //
    return 1;
  }
  else {
    //
    // Get the AND pointer again.
    //
    lpbSrc=(LPBYTE)GlobalLock(pIE->hMemAND);

    //
    // Create the icon now that we have all the data.  lpbDst already
    // points to the XOR bits.
    // 
    pIE->hIcon=CreateIcon(pIE->hInst, uWidth, uHeight, (BYTE)bm.bmPlanes,
                         (BYTE)bm.bmBitsPixel, (LPVOID)lpbSrc,
                         (LPVOID)lpbDst);

    GlobalUnlock(pIE->hMemAND);
    GlobalFree(pIE->hMemAND);
    GlobalFree(hMem);

    //
    // We're done so we can stop.
    //
    return 0;
  }
}

BOOL OleUIMetafilePictExtractIconSource(HGLOBAL hMetaPict, 
                                        LPSTR lpszSource, 
                                        UINT FAR *piIcon)
{
  LPMETAFILEPICT  pMF;
  TLabelExtract    le;
  HDC             hDC;

  //
  // We will walk the metafile looking for the two comment records
  // following the IconOnly comment.  The flags fFoundIconOnly and
  // fFoundSource indicate if we have found IconOnly and if we have
  // found the source comment already.
  // 
  if (NULL==hMetaPict || NULL==lpszSource || NULL==piIcon)
    return FALSE;

  pMF=(LPMETAFILEPICT)GlobalLock(hMetaPict);

  if (NULL==pMF)
    return FALSE;

  le.lpsz=lpszSource;
  le.fFoundIconOnly=FALSE;
  le.fFoundSource=FALSE;
  le.fFoundIndex=FALSE;

  //
  // Use a screen DC so we have something valid to pass in.
  //
  hDC=GetDC(NULL);
  EnumMetaFile(hDC, pMF->hMF, (MFENUMPROC)EnumMetafileExtractIconSource, 
              (LONG)(TLabelExtract far*)&le);
  ReleaseDC(NULL, hDC);

  GlobalUnlock(hMetaPict);

  //
  // Copy the icon index to the caller's variable.
  //
  *piIcon=le.u.iIcon;
  
  //
  // Check that we found everything.
  //
  return (le.fFoundIconOnly && le.fFoundSource && le.fFoundIndex);
}

int CALLBACK __export
EnumMetafileExtractIconSource(HDC /*hDC*/,
                              HANDLETABLE FAR* /*phTable*/,
                              METARECORD FAR *pMFR,
                              int /*cObj*/, 
                              TLabelExtract far* pLE)
{
  LPSTR psz;

  //
  // We don't allow anything to happen until we see "IconOnly"
  // in an MFCOMMENT that is used to enable everything else.
  // 
  if (!pLE->fFoundIconOnly) {
    if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0]) {
      if (0==lstrcmpi(szIconOnly, (LPSTR)&pMFR->rdParm[2]))
        pLE->fFoundIconOnly=TRUE;
      }

    return 1;
  }

  //
  // Now see if we find the source string.
  //
  if (!pLE->fFoundSource) {
    if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0]) {
      strcpyn(pLE->lpsz, (LPSTR)&pMFR->rdParm[2], MaxPathLen);
      pLE->lpsz[MaxPathLen-1] = '\0';
      pLE->fFoundSource=TRUE;
    }

    return 1;
  }

  //
  // Next comment will be the icon index.
  //
  if (META_ESCAPE==pMFR->rdFunction && MFCOMMENT==pMFR->rdParm[0]) {
    //
    // This string contains the icon index in string form,
    // so we need to convert back to a UINT.  After we see this
    // we can stop the enumeration.  The comment will have
    // a null terminator because we made sure to save it.
    //
    psz=(LPSTR)&pMFR->rdParm[2];
    pLE->u.iIcon=0;

    //
    // Do Ye Olde atoi
    //
    while (*psz)
      pLE->u.iIcon=(10*pLE->u.iIcon)+((*psz++)-'0');

    pLE->fFoundIndex=TRUE;
    return 0;
  }

  return 1;
}

//
// Aliases an OLE MetafilePictIcon
//
TOleMetaPict::TOleMetaPict(HGLOBAL metaPict, TAutoDelete autoDelete):
              MetaPict(metaPict), ShouldDelete(autoDelete==AutoDelete)
{}

//
// Frees Metafile and Memory handle (if ShouldDelete)
//
TOleMetaPict::~TOleMetaPict()
{
  if (ShouldDelete)
    Free(MetaPict);
}

//
// Frees the Metafile Handle and Global Memory Handle
//
bool
TOleMetaPict::Free(HGLOBAL& metaPict)
{
  if (!metaPict)
    return false;

  LPMETAFILEPICT pMF = (LPMETAFILEPICT)GlobalLock(metaPict);
  if (!pMF) {
    TRACEX(OwlOleDlg, 1, "Unable to lock MetaPict Handle");
    return false;
  }

  if (pMF->hMF)
    DeleteMetaFile(pMF->hMF);

  GlobalUnlock(metaPict);
  GlobalFree(metaPict);
  metaPict = 0;
  return true;
}

//
//
//
bool
TOleMetaPict::Draw(TDC& dc, TRect& rect, bool iconOnly) const
{
  PRECONDITION(MetaPict);
  return OleUIMetafilePictIconDraw(dc, &rect, MetaPict, iconOnly) ?
         true : false;  
}

//
//
//
int
TOleMetaPict::ExtractLabel(char far* label, uint32 *wrap) const
{
  PRECONDITION(MetaPict);
  return OleUIMetafilePictExtractLabel(MetaPict, label, MaxLabelLen, wrap);
}

//
//
//
HICON
TOleMetaPict::ExtractIcon(HINSTANCE hinst) const
{
  PRECONDITION(MetaPict);
  return OleUIMetafilePictExtractIcon(MetaPict, hinst);
}

//
//
//
bool
TOleMetaPict::ExtractIconSource(char far* source, uint& index) const
{
  PRECONDITION(MetaPict);
  return OleUIMetafilePictExtractIconSource(MetaPict, source, &index);
}
