//----------------------------------------------------------------------------
// ObjectWindows
// (C) Copyright 1991, 1994 by Borland International, All Rights Reserved
//
 //   Defines type TOleDialog, the base class of all OLE Dialog classes
//----------------------------------------------------------------------------
#define INC_OLE2
#include <owl/owlpch.h>
#include <owl/dialog.h>
#include <owl/opensave.h>
#include <owl/listbox.h>
#include <owl/radiobut.h>
#include <owl/checkbox.h>
#include <owl/groupbox.h>
#include <owl/edit.h>
#include <ocf/ocview.h>
#include <dir.h>
#include <owl/oledlg.h>

//
// Owl OLE Dialog general diagnostic group
//
DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlOleDlg, 1, 0);

//
// Helper variables
//
extern const char ClsIdStr[] = "\\CLSID";
const  char PrefixFmt[] = "%c%c%c...\\";

//
// Returns OBJECTDESCRIPTOR handle
//
HGLOBAL
TOleDialog::GetObjectDescriptorData(CLSID clsid, DWORD dwDrawAspect,
                                    SIZEL sizel, POINTL pointl,
                                    DWORD dwStatus,
                                    LPSTR lpszFullUserTypeName,
                                    LPSTR lpszSrcOfCopy)
{
  //
  // Length of full user name
  //
  DWORD dwFullUserTypeNameLen = lpszFullUserTypeName ?
                  strlen(lpszFullUserTypeName)+1 : 0;

  //
  // Length of source copy string
  //
  DWORD dwSrcOfCopyLen;
  if (lpszSrcOfCopy)
    dwSrcOfCopyLen = strlen(lpszSrcOfCopy)+1;
  else {
    //
    // User user type name as source string
    //
    lpszSrcOfCopy =  lpszFullUserTypeName;
    dwSrcOfCopyLen = dwFullUserTypeNameLen;
  }

  //
  // Allocate space for OBJECTDESCRIPTOR and the additional string data
  //
  DWORD dwObjectDescSize = sizeof(OBJECTDESCRIPTOR);
  HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
                             dwObjectDescSize + dwFullUserTypeNameLen +
                             dwSrcOfCopyLen);

  //
  // Bail out of memory failure
  //
  if (!hMem)
    return 0;

  LPOBJECTDESCRIPTOR lpOD = (LPOBJECTDESCRIPTOR)GlobalLock(hMem);

  //
  // Set the FullUserTypeName offset and copy the string
  //
  if (lpszFullUserTypeName) {
    lpOD->dwFullUserTypeName = dwObjectDescSize;
    strcpy((LPSTR)lpOD + (int)lpOD->dwFullUserTypeName, lpszFullUserTypeName);
  }
  else {
     //
     // zero offset indicates that string is not present
     //
    lpOD->dwFullUserTypeName = 0;
  }

  //
  // Set the SrcOfCopy offset and copy the string
  //
  if (lpszSrcOfCopy) {
    lpOD->dwSrcOfCopy = dwObjectDescSize + dwFullUserTypeNameLen;
    strcpy((LPSTR)lpOD+(int)lpOD->dwSrcOfCopy , lpszSrcOfCopy);
  }
  else {
    //
    // zero offset indicates that string is not present
    //
    lpOD->dwSrcOfCopy = 0;
  }

  //
  // Initialize the rest of the OBJECTDESCRIPTOR
  //
  lpOD->cbSize       = dwObjectDescSize + dwFullUserTypeNameLen +
                       dwSrcOfCopyLen;
  lpOD->clsid        = clsid;
  lpOD->dwDrawAspect = dwDrawAspect;
  lpOD->sizel        = sizel;
  lpOD->pointl       = pointl;
  lpOD->dwStatus     = dwStatus;

  GlobalUnlock(hMem);
  return hMem;
}

//
// Retrieve data from an IDataObject in a specified format on a
// global memory block. This function ALWAYS returns a private copy
// of the data to the caller. if necessary a copy is made of the
// data (ie. if lpMedium->pUnkForRelease != 0). The caller assumes
// ownership of the data block in all cases and must free the data
// when done with it. The caller may directly free the data handle
// returned (taking care whether it is a simple HGLOBAL or a HANDLE
// to a MetafilePict) or the caller may call
// ReleaseStgMedium(lpMedium). this OLE helper function will do the
// right thing.
//
HGLOBAL
TOleDialog::GetData(LPDATAOBJECT lpDataObj, CLIPFORMAT cfFormat,
                    DVTARGETDEVICE FAR* lpTargetDevice,
                    DWORD dwDrawAspect, LPSTGMEDIUM lpMedium)
{
  FORMATETC formatetc;

  // Init FORMATETC structure
  //
  DWORD tymed;
  switch (cfFormat) {
    case CF_METAFILEPICT:
      tymed = TYMED_MFPICT;
      break;

    case CF_BITMAP:
      tymed = TYMED_GDI;
      break;

    default:
      tymed = TYMED_HGLOBAL;
      break;
  }
  InitFormatEtc(formatetc, cfFormat, tymed, -1, dwDrawAspect, lpTargetDevice);

  TRACEX(OwlOleDlg, 1, "IDataObject::GetData called");
  if (lpDataObj->GetData((LPFORMATETC)&formatetc, lpMedium) != NOERROR)
    return 0;

  HGLOBAL hGlobal = lpMedium->hGlobal;  //!CQ nonamelessunion? u.
  if (!hGlobal)
    return 0;

  //
  // Check if hGlobal really points to valid memory
  //
  LPVOID lp = GlobalLock(hGlobal);
  if (lp) {
    if (IsBadReadPtr(lp, 1)) {
      GlobalUnlock(hGlobal);
      return 0;
    }
    GlobalUnlock(hGlobal);
  }

  if (hGlobal != 0 && lpMedium->pUnkForRelease != 0) {
    //
    // OLE2NOTE: the callee wants to retain ownership of the data.
    //    this is indicated by passing a non-0 pUnkForRelease.
    //    thus, we will make a copy of the data and release the
    //    callee's copy.
    //
    HGLOBAL hCopy;
    hCopy = OleDuplicateData(hGlobal, cfFormat, GHND|GMEM_SHARE);
    ReleaseStgMedium(lpMedium);

    hGlobal = hCopy;
    lpMedium->hGlobal = hCopy;    //!CQ nonamelessunion? u.
    lpMedium->pUnkForRelease = 0;
  }
  return hGlobal;
}

//
// Fills and returns a OBJECTDESCRIPTOR structure. The source object will
// offer CF_OBJECTDESCRIPTOR if it is an OLE2 object, CF_OWNERLINK if it
// is an OLE1 object, or CF_FILENAME if it has been copied to the clipboard
// by FileManager.
//
HGLOBAL
TOleDialog::FillObjectDescriptorFromData(LPDATAOBJECT lpDataObject,
                                         LPSTGMEDIUM  lpmedium,
                                         CLIPFORMAT far* lpcfFmt)
{
  CLSID              clsid;
  SIZEL              sizelHim;
  POINTL             pointl;
  LPSTR              lpsz, szFullUserTypeName, szSrcOfCopy, 
                     szClassName, szDocName, szItemName;
  int                nClassName, nDocName, nItemName, nFullUserTypeName;
  LPSTR              szBuf = 0;
  HKEY               hKey = 0;
  DWORD              dw = MaxPathLen;  
  HGLOBAL            hObjDesc;
  HRESULT            hrErr;

  //
  // GetData CF_OBJECTDESCRIPTOR format from the object on the clipboard.
  // Only OLE 2 objects on the clipboard will offer CF_OBJECTDESCRIPTOR
  //
  HGLOBAL hMem = GetData(lpDataObject, cfObjectDescriptor, 0, DVASPECT_CONTENT, lpmedium);

  if (hMem != 0) {
    *lpcfFmt = cfObjectDescriptor;
    return hMem;
  }
  else if ((hMem = GetData(lpDataObject,  cfOwnerLink, 
                           0, DVASPECT_CONTENT, lpmedium)) != 0) {
    //
    // If CF_OBJECTDESCRIPTOR is not available, i.e. if this is not an
    // OLE2 object, check if this is an OLE 1 object. OLE 1 objects
    // will offer CF_OWNERLINK
    //
    *lpcfFmt = cfOwnerLink;

    //
    // CF_OWNERLINK contains 0-terminated strings for class name,
    // document name and item name with two 0 terminating characters
    // at the end
    //
    szClassName = (LPSTR)GlobalLock(hMem);
    nClassName  = strlen(szClassName);
    szDocName   = szClassName + nClassName + 1;
    nDocName    = strlen(szDocName);
    szItemName  = szDocName + nDocName + 1;
    nItemName   = strlen(szItemName);

    //
    // Find FullUserTypeName from Registration database using class name
    //
    if (RegOpenKey(HKEY_CLASSES_ROOT, 0, &hKey) != ERROR_SUCCESS)
       goto error;

    //
    // Allocate space for szFullUserTypeName & szSrcOfCopy. 
    // Maximum length of FullUserTypeName is MaxKeyLen. 
    // SrcOfCopy is constructed by concatenating FullUserTypeName, 
    // Document Name and ItemName separated by spaces.
    //
    TOleAllocator allocator;
    szBuf = (LPSTR)allocator.Alloc((DWORD)2*MaxKeyLen+nDocName+nItemName+4);
    if (!szBuf)
      goto error;

    szFullUserTypeName = szBuf;
    szSrcOfCopy = szFullUserTypeName+MaxKeyLen+1;

    //
    // Get FullUserTypeName
    //
    if (::RegQueryValue(hKey, 0, szFullUserTypeName, (LONG*)&dw) != ERROR_SUCCESS)
       goto error;

    //
    // Build up SrcOfCopy string from FullUserTypeName, DocumentName & ItemName
    //
    lpsz = szSrcOfCopy;
    lstrcpy(lpsz, szFullUserTypeName);
    nFullUserTypeName = lstrlen(szFullUserTypeName);
    lpsz[nFullUserTypeName]=' ';
    lpsz += nFullUserTypeName+1;
    lstrcpy(lpsz, szDocName);
    lpsz[nDocName] = ' ';
    lpsz += nDocName+1;
    lstrcpy(lpsz, szItemName);

    sizelHim.cx = sizelHim.cy = 0;
    pointl.x = pointl.y = 0;

    CLSIDFromProgID(OleStr(szClassName), &clsid);

    hObjDesc = GetObjectDescriptorData(clsid, DVASPECT_CONTENT, sizelHim,
                                       pointl, 0, szFullUserTypeName,
                                       szSrcOfCopy);
    if (!hObjDesc)
       goto error;

  //
  // Check if object is CF_FILENAME
  //
  }
  else if ((hMem = GetData(lpDataObject, cfFileName, 0,  DVASPECT_CONTENT,
                           lpmedium)) != 0) {
    *lpcfFmt = cfFileName;
    lpsz = (LPSTR)GlobalLock(hMem);
    hrErr = GetClassFile(OleStr(lpsz), &clsid);

    //
    // OLE2NOTE: if the file does not have an OLE class
    //    associated, then use the OLE 1 Packager as the class of
    //    the object to be created. this is the behavior of
    //    OleCreateFromData API
    //
    if (hrErr != NOERROR)
      CLSIDFromProgID(OleText("Package"), &clsid);
    sizelHim.cx = sizelHim.cy = 0;
    pointl.x = pointl.y = 0;

    TOleAllocator allocator;
    szBuf = (LPSTR)allocator.Alloc(MaxKeyLen);
    if (0 == szBuf)
      goto error;

    GetUserTypeOfClass(clsid, szBuf, 0);

    hObjDesc = GetObjectDescriptorData(clsid, DVASPECT_CONTENT, sizelHim,
                                       pointl, 0, szBuf, lpsz);
    if (!hObjDesc)
      goto error;
  }
  else {
    TRACEX(OwlOleDlg, 1, "Unable to retrieve OBJECTDESCRIPTOR info.");
    goto error;
  }

  //
  // Clean up
  //
  if (szBuf)
    TOleAllocator().Free((LPVOID)szBuf);

  if (hMem) {
    GlobalUnlock(hMem);
    GlobalFree(hMem);
  }

  if (hKey) {
    RegCloseKey(hKey);
  }

  return hObjDesc;

error:
  if (szBuf)
    TOleAllocator().Free((LPVOID)szBuf);

  if (hMem) {
    GlobalUnlock(hMem);
    GlobalFree(hMem);
  }

  if (hKey) {
    RegCloseKey(hKey);
  }

  return 0;
}

//
// Returns the specified AuxUserType from the reg db.
//
uint
TOleDialog::GetAuxUserType(REFCLSID rclsid, WORD wAuxUserType,
                           LPSTR lpszAuxUserType, int cch, HKEY hKey)
{
  bool     fCloseRegDB = false;
  HKEY     hThisKey;
  if (!hKey) {
    if (::RegOpenKey(HKEY_CLASSES_ROOT, 0, &hThisKey) != ERROR_SUCCESS)
      return 0;
    fCloseRegDB = true;
  }
  else {
    hThisKey = hKey;
  }

  LPOLESTR lpszCLSID;
  StringFromCLSID(rclsid, &lpszCLSID);

  char     szKey[MaxKeyLen];     
  lstrcpy(szKey, "CLSID\\");
  lstrcat(szKey, OleStr(lpszCLSID));
  char     szTemp[32];
  wsprintf(szTemp, "\\AuxUserType\\%d", wAuxUserType);
  lstrcat(szKey, szTemp);

  lpszAuxUserType[0] = '\0';
  LONG dw = cch;
  LRESULT lRet = ::RegQueryValue(hThisKey, szKey, lpszAuxUserType, &dw);

  if (lRet != ERROR_SUCCESS) {
    dw = 0;
    lpszAuxUserType[0] = '\0';
  }

  if (fCloseRegDB)
    RegCloseKey(hThisKey);

  TOleAllocator().Free((LPVOID)lpszCLSID);

  return (UINT)dw;
}

//
//
//
uint
TOleDialog::GetUserTypeOfClass(REFCLSID clsid, char far* lpszUserStr, uint len)
{
  if (!lpszUserStr)
    return 0;

  HKEY hKey;
  if (RegOpenKey(HKEY_CLASSES_ROOT, 0, &hKey) != ERROR_SUCCESS)
    return 0;

  LPOLESTR lpszClsId;
  bool freeClsId = HRIsOK(StringFromCLSID(clsid, &lpszClsId));

  char  szKey[MaxPathLen];
  strcpy(szKey, ::ClsIdStr);
  if (freeClsId)
    strcat(szKey, OleStr(lpszClsId));

  LONG buffSize = len;
  LPOLESTR lpszProgId;
  bool  freeProgId = false;
  if (::RegQueryValue(hKey, szKey, lpszUserStr, &buffSize) != ERROR_SUCCESS) {
    //
    // Check for OLE 1.0 class
    //
    if (CoIsOle1Class(clsid)) {
      freeProgId = HRIsOK(ProgIDFromCLSID(clsid, &lpszProgId));
      if (freeProgId) {
        buffSize = len;
        if (::RegQueryValue(hKey, OleStr(lpszProgId),
                          lpszUserStr, &buffSize) != ERROR_SUCCESS)
          buffSize = 0;
      }
    }
  }

  TOleAllocator oleAllocator;
  if (freeClsId)
    oleAllocator.Free(lpszClsId);

  if (freeProgId)
    oleAllocator.Free(lpszProgId);

  RegCloseKey(hKey);
  return (uint)buffSize;
}

//
// DoesFileExist
// -------------
//  Tests for the existence via OpenFile [which opens and closes the file]
//  Returns HFILE_ERROR if an error occurs with ofs.nErrCode containing an
//  error value.
//
HFILE
TOleDialog::DoesFileExist(const char* filename, OFSTRUCT& ofs)
{
  //
  // The following check for reserved MS-DOS device names is necessary
  // because OleCreateFromFile() does not perform such check. More
  // information about this issue is available on the MSDN CD, article
  // 'OleCreateFile() Does Not Check for Reserved Names' - PSS ID# Q111015.
  //
  static char *illegalNames[] = {"LPT1", "LPT2", "LPT3",
                                 "COM1", "COM2", "COM3", "COM4",
                                 "CON", "AUX", "PRN"};
  for (int i=0; i<sizeof(illegalNames)/sizeof(illegalNames[0]); i++) {
    if (strcmpi(filename, illegalNames[i]) == 0) {
      memset(&ofs, 0, sizeof(ofs));
      ofs.nErrCode = 0x0002;  // File not found
      return HFILE_ERROR;
    }
  }

  return OpenFile(filename, &ofs, OF_EXIST);
}

//
// Returns a pointer to the beginning of the nth field of a string -
// Fields are delimited by 'chDelim'
//
LPSTR
TOleDialog::PtrToNthField(LPSTR lpszStr, int nField, char chDelim)
{
  LPSTR lpszField = lpszStr;
  int    cFieldFound = 1;

  if (nField == 1)
    return lpszStr;

  while(*lpszField) {
    if (*lpszField++ == chDelim) {
      cFieldFound++;
      if (cFieldFound == nField)
        return lpszField;
    }
  }
  return lpszField;
}

//
// TOleDialog's Response Table
//
DEFINE_RESPONSE_TABLE1(TOleDialog, TDialog)
  EV_WM_CLOSE,
  EV_COMMAND(IDOK, CmOk),
  EV_COMMAND(IDCANCEL, CmCancel),
END_RESPONSE_TABLE;

//
// Registered Clipboard Formats
//
uint16 TOleDialog::cfObjectDescriptor = 0;
uint16 TOleDialog::cfLinkSrcDescriptor= 0;
uint16 TOleDialog::cfEmbedSource      = 0;
uint16 TOleDialog::cfEmbeddedObject   = 0;
uint16 TOleDialog::cfLinkSource       = 0;
uint16 TOleDialog::cfOwnerLink        = 0;
uint16 TOleDialog::cfFileName         = 0;
bool   TOleDialog::cfInit             = false;

//
//
//
TOleDialog::TOleDialog(TWindow*        parent,
                       TResId          templateId,
                       const char far* title,
                       TModule*        module)
:
  TDialog(parent, templateId, module),
  ODTitle(title), Font(0)
{
  //
  // Register  Clipboard Formats
  //
  if (!cfInit) {
    cfObjectDescriptor  = (uint16)::RegisterClipboardFormat(CF_OBJECTDESCRIPTOR);
    cfLinkSrcDescriptor = (uint16)::RegisterClipboardFormat(CF_LINKSRCDESCRIPTOR);
    cfEmbedSource       = (uint16)::RegisterClipboardFormat(CF_EMBEDSOURCE);
    cfEmbeddedObject    = (uint16)::RegisterClipboardFormat(CF_EMBEDDEDOBJECT);
    cfLinkSource        = (uint16)::RegisterClipboardFormat(CF_LINKSOURCE);
    cfOwnerLink         = (uint16)::RegisterClipboardFormat(CF_OWNERLINK);
    cfFileName          = (uint16)::RegisterClipboardFormat(CF_FILENAME);
    cfInit = true;
  }
}

//
// Creates non-bold font for static controls and update dialog's title
// if user specified one.
//
void
TOleDialog::SetupWindow()
{
    TDialog::SetupWindow();

    //
    // Create non-bold font for text displays
    //
    HFONT   hfont = GetWindowFont();
    LOGFONT lf;
    GetObject(hfont, sizeof(LOGFONT), &lf);
    lf.lfWeight=FW_NORMAL;
    Font = new TFont(&lf);

    //
    // Override title if user specified one
    //
    if (ODTitle)
      SetCaption(ODTitle);
}

//
// Cleanup Font
//
void
TOleDialog::CleanupWindow()
{
  if (Font) {
    delete Font;
    Font = 0;
  }
}

//
// Enables and shows [or disables and hides] a [child] window
//
void
TOleDialog::Activate(TWindow *win, bool activate)
{
  win->Show(activate ? SW_SHOW : SW_HIDE);
  win->EnableWindow(activate);
}

//
// Responds to OK button by invoking virtual method prior before chaining
// to base class CmOk if everything's OK - This allows the derived OLE-DLG
// to 'veto' the closure of the dialog.
//
void
TOleDialog::CmOk()
{
  if (OleDlgOk()) {
    TDialog::CmOk();
  }
}

//
// Displays a MessageBox informing user of an error resulting
// from a file operation.
//
int
TOleDialog::ErrorWithFile(uint strId, const char* fileName, uint mbFlags)
{
  const int BuffLen = MaxPathLen*2;
  TPointer<char>  pstr1 = new char[BuffLen];
  TPointer<char>  pstr2 = new char[BuffLen];
  TPointer<char>  pstr3 = new char[BuffLen];

  if (GetModule()->LoadString(strId, pstr1, BuffLen)) {
    wsprintf((LPSTR)(char*)pstr2, (LPSTR)(char*)pstr1, (LPSTR)fileName);
    GetWindowText((LPSTR)(char*)pstr3, BuffLen);
    return MessageBox(pstr2, pstr3, mbFlags);
  }

  return 0;
}

//
// Invokes ErrorWithFile [see above] with the appropriate string resource
// Id. based on the Error Code specified.
//
void
TOleDialog::OpenFileError(uint errCode, const char* fileName)
{
  switch (errCode) {
    case  0x0005:   // Access denied
          ErrorWithFile(IDS_CIFILEACCESS, fileName);
          break;

    case  0x0020:   // Sharing Violation
          ErrorWithFile(IDS_CIFILESHARE, fileName);
          break;

    case  0x0002:   // File not found
    case  0x0003:   // Path not found
          ErrorWithFile(IDS_CIINVALIDFILE, fileName);
          break;

    default:        // Other failures
          ErrorWithFile(IDS_CIFILEOPENFAIL, fileName);
          break;
  }
}

//
// Displays the standard FileOpen with the 'Browse' caption.
//
bool
TOleDialog::BrowseDlg(char* file, char* initDir, uint filterId, uint32 flags)
{
  //
  // Retrieve filters from resource if specified
  //
  char  filterBuff[256];
  char *filterPtr = 0;
  if (filterId)
    if (GetModule()->LoadString(filterId, filterBuff, sizeof(filterBuff)))
      filterPtr = filterBuff;

  //
  // Retrieve dialog's title from resource
  //
  char  titleBuff[128];
  char *titlePtr = 0;
  if (GetModule()->LoadString(IDS_BROWSE, titleBuff, sizeof(titleBuff)))
    titlePtr = titleBuff;

  //
  // Create/Init TFileOpenDialog data structure
  //
  TOpenSaveDialog::TData *data;
  data = new TOpenSaveDialog::TData(flags, filterPtr, 0, initDir);

  //
  // Execute dialog and return status [copying new filename if necessary]
  //
  TFileOpenDialog fopenDlg(this, *data, 0, titlePtr);
  int ret = fopenDlg.Execute();

  if (ret == IDOK)
    strcpyn(file, data->FileName, MaxPathLen);

  delete data;
  return (ret == IDOK) ? true : false;
}

//
//
//
char far*
TOleDialog ::ChopText(TWindow &ctl, int width, char far* lpch)
{
  //
  // Use client area's width if 0 width specified
  //
  if (!width) {
    TRect rect;
    ctl.GetClientRect(rect);
    width = rect.Width();
  }

  //
  // Grab control's font
  //
  HFONT hFont = ctl.GetWindowFont();
  if (!hFont)
    hFont = (HFONT)GetStockObject(SYSTEM_FONT);
  TFont font(hFont);

  //
  // Graf control's DC and select font
  //
  TClientDC hdc(ctl);
  hdc.SelectObject(font);

  if (GetTextWSize(hdc, lpch) > width) {
    char szPrefix[PrefixSize];
    wsprintf(szPrefix, PrefixFmt, lpch[0], lpch[1], lpch[2]);
    width -= GetTextWSize(hdc, szPrefix);

    bool done = false;
    while (!done) {
      while(*lpch  &&  (*lpch != '\\'))
        lpch = AnsiNext(lpch);
      if (*lpch)
        lpch = AnsiNext(lpch);

      if (!*lpch  ||  GetTextWSize(hdc, lpch) <= width) {
        if (!*lpch)
          szPrefix[strlen(szPrefix)-1] = 0;

        for (int i=strlen(szPrefix)-1; i>=0; --i)
          *--lpch = szPrefix[i];

        done = true;
      }
    }
  }

  hdc.RestoreFont();
  return lpch;
}

//
//
//
int
TOleDialog::GetTextWSize(TDC &dc, char far* lpsz)
{
  TSize size;
  if (dc.GetTextExtent(lpsz, strlen(lpsz), size))
    return size.cx;
  else
    return 0;
}

//
//
//
int
TOleDialog::PopupMessage(uint msgId, uint titleId, uint mbFlags)
{
  TPointer<char>  pTitle = new char[LoadStringLen];
  TPointer<char>  pMsg   = new char[LoadStringLen];

  GetModule()->LoadString(msgId, pTitle, LoadStringLen);
  GetModule()->LoadString(titleId, pTitle, LoadStringLen);

  return MessageBox(pMsg, pTitle, mbFlags);
}


DEFINE_RESPONSE_TABLE1(TIconImage, TControl)
  EV_WM_PAINT,
  EV_WM_ERASEBKGND,
END_RESPONSE_TABLE;

//
//
//
TIconImage::TIconImage(TWindow* parent, int resourceId, TModule* module)
           :TControl(parent, resourceId, module), MetaPict(0)
{}

//
//
//
void
TIconImage::GetWindowClass(WNDCLASS& wndclass)
{
  TControl::GetWindowClass(wndclass);

  wndclass.hbrBackground = 0;
  wndclass.style = 0;
}

//
//
//
bool
TIconImage::SetMetaPict(HGLOBAL metaPict, bool deleteOld)
{
  HGLOBAL oldMeta = MetaPict;
  MetaPict = metaPict;

  Invalidate();
  UpdateWindow();

  if (deleteOld) {
    if (oldMeta) {
      return TOleMetaPict::Free(oldMeta);
    }
    else {
      TRACEX(OwlOleDlg, 1, "Old handle is 0 and was not deleted");
      return false;
    }
  }
  return true;
}

//
//
//
void
TIconImage::EvPaint()
{
  TPaintDC dc(*this);
  TRect clientRect;
  GetClientRect(clientRect);

  TOleMetaPict oleMetaPict(MetaPict);
  oleMetaPict.Draw(dc, clientRect, false);
}

//
//
//
bool
TIconImage::EvEraseBkgnd(HDC hdc)
{
  HBRUSH  hBrush;
#if defined(BI_PLAT_WIN32)
  hBrush = (HBRUSH)Parent->SendMessage(WM_CTLCOLORDLG, (WPARAM)hdc,
                                       (LPARAM)GetParent());
#else
  hBrush = (HBRUSH)Parent->SendMessage(WM_CTLCOLOR, (WPARAM)hdc,
                                      MAKELPARAM(GetParent(), CTLCOLOR_DLG));
#endif

  if (!hBrush)
    return false;

  TBrush brush(hBrush);
#if defined(BI_PLAT_WIN16)
  brush.UnrealizeObject();
#endif

  TDC dc(hdc);
  dc.SetBrushOrg(0, 0);

  TRect rect;
  GetClientRect(rect);
  dc.FillRect(rect, brush);
  return true;
}

//
// Constants
//
const long RopDSPDxax = 0x00E20746L;

//
//
//
DEFINE_RESPONSE_TABLE1(TResultImage, TControl)
  EV_WM_PAINT,
END_RESPONSE_TABLE;

//
// Init variables
//
TResultImage::TResultImage(TWindow* parent, int resourceId, TModule* module)
             :TControl(parent, resourceId, module)
{
  Bitmap = 0;
  ShouldDelete = false;
}

//
// Clean up bitmap (if necessary)
//
TResultImage::~TResultImage()
{
  SetBitmap(0);
}

//
//
//
void
TResultImage::GetWindowClass(WNDCLASS& wndclass)
{
  TControl::GetWindowClass(wndclass);

  wndclass.hbrBackground = 0;
  wndclass.style = CS_VREDRAW | CS_HREDRAW;
}

//
//
//
void
TResultImage::SetupWindow()
{
  TControl::SetupWindow();

  //
  // Init Result Bitmap
  //
  TResId bmpId;
  TScreenDC screenDC;
  int cx = screenDC.GetDeviceCaps(LOGPIXELSY);
  if (cx <= 72)                 bmpId = IDB_RESULTSEGA;
  if (cx > 72  &&  cx < 120)    bmpId = IDB_RESULTSVGA;
  if (cx >= 120)                bmpId = IDB_RESULTSHIRESVGA;
  SetBitmap(bmpId, TColor::LtCyan, 7, 0);
}

//
//
//
void
TResultImage::SetBitmap(TResId bmpId, TColor transparentColor,
                        uint numRows, uint curIndex, TAutoDelete autoDelete)
{
  //
  // Cleanup current bitmap if 'shouldDelete' is enabled
  //
  if (Bitmap  &&  ShouldDelete)
  {
    delete Bitmap;
    Bitmap = 0;
  }

  //
  // Retrieve new bitmap if necessary
  //
  if (bmpId != 0)
  {
    ShouldDelete = autoDelete == AutoDelete ? true : false;
    Bitmap = new TBitmap(*GetModule(), bmpId);

    NumRows   = numRows;
    CurIndex  = curIndex;
    ClearColor= transparentColor;

    CX = Bitmap->Width();
    CY = Bitmap->Height();

    if (numRows)
      CY /= numRows;

    SetBitmapIndex(curIndex);   // Invalidates the window
  }
}

//
//
//
void
TResultImage::SetBitmapIndex(uint curIndex)
{
  PRECONDITION(Bitmap);
  PRECONDITION(curIndex < NumRows);

  CurIndex = curIndex;

  //
  // Force repaint
  //
  Invalidate(false);
  UpdateWindow();
}

//
//
//
void
TResultImage::EvPaint()
{
  TPaintDC dc(*this);

  //
  // Retrieve background brush of parent
  //
  HBRUSH  hBrush = 0;

  {
  TClientDC parentDC(*Parent);
#if defined(BI_PLAT_WIN32)
  hBrush = (HBRUSH)Parent->SendMessage(WM_CTLCOLORDLG, (WPARAM)(HDC)parentDC,
                                       (LPARAM)GetParent());
#else
  hBrush = (HBRUSH)Parent->SendMessage(WM_CTLCOLOR, (WPARAM)(HDC)parentDC,
                                      MAKELPARAM(GetParent(), CTLCOLOR_DLG));
#endif
  }

  //
  // Make our DC's background color match parent's background
  //
  if (hBrush)
  {
    LOGBRUSH lb;
    GetObject(hBrush, sizeof(LOGBRUSH), &lb);
    dc.SetBkColor(TColor(lb.lbColor));
  }

  if (!Bitmap)
    return;

  //
  // Destination Coordinates for bitmap: Center bitmap
  //
  uint dstX =0, dstY =0;
  TRect rect;
  GetClientRect(rect);
  if ((rect.right + rect.left) > CX)
    dstX = (rect.right + rect.left - CX)/2;
  if ((rect.bottom + rect.top) > CY)
    dstY = (rect.bottom + rect.top - CY)/2;

  //
  // Source Coordinates: Offset to appropriate row for source
  //
  uint srcX, srcY;
  srcX = 0;
  srcY = CY * CurIndex;

  //
  // Three intermediate memory DCs
  //
  TMemoryDC hdcSrc(dc), hdcMid(dc), hdcMem(dc);

  //
  // Select our bitmap in a Source MemDC [for bitblt]
  //
  hdcSrc.SelectObject(*Bitmap);

  //
  // Create monochrome bitmap for masking
  //
  TBitmap bmpMono(hdcMid, CX, CY);
  hdcMid.SelectObject(bmpMono);

  //
  // Create 'middle' bitmap
  //
  TBitmap bmpMid(dc, CX, CY);
  hdcMem.SelectObject(bmpMid);

  //
  // Create monochrome mask
  //
  TColor bkColor = hdcSrc.SetBkColor(ClearColor);
  hdcMid.BitBlt(0, 0, CX, CY, hdcSrc, srcX, srcY);
  hdcSrc.SetBkColor(bkColor);

  //
  // Save unmodified image in temporary bitmap
  //
  hdcMem.BitBlt(0, 0, CX, CY, hdcSrc, srcX, srcY);

  //
  // Create/Select background color brush
  //
  TBrush bkBrush(dc.GetBkColor());
  hdcMem.SelectObject(bkBrush);

  //
  // Force conversion of monochrome to stay black & white
  //
  hdcMem.SetTextColor(TColor(0));
  hdcMem.SetBkColor(TColor(255, 255, 255));

  //
  // Blt brush where monochrome mask is '1'; Leave destination untouch
  // where it's '0'.
  //
  hdcMem.BitBlt(0, 0, CX, CY, hdcMid, 0, 0, RopDSPDxax);
  dc.BitBlt(dstX, dstY, CX, CY, hdcMem, 0, 0);

  //
  // Restore DC attributes
  //
  hdcMem.RestoreBrush();
  hdcMem.RestoreBitmap();
  hdcMid.RestoreBitmap();
}
