/*
 * PASTESPL.C
 *
 * Implements the OleUIPasteSpecial function which invokes the complete
 * Paste Special dialog.
 *
 * Copyright (c)1992 Microsoft Corporation, All Rights Reserved
 */

#define STRICT  1
#include "ole2ui.h"
#include "pastespl.h"
#include "common.h"
#include "utility.h"
#include "resimage.h"
#include "iconbox.h"
#include "geticon.h"
#include "icon.h"
#include "regdb.h"
#include <stdlib.h>

OLEDBGDATA

/*
 * OleUIPasteSpecial
 *
 * Purpose:
 *  Invokes the standard OLE Paste Special dialog box which allows the user
 *  to select the format of the clipboard object to be pasted or paste linked.
 *
 * Parameters:
 *  lpPS         LPOLEUIPasteSpecial pointing to the in-out structure
 *               for this dialog.
 *
 * Return Value:
 *  UINT        One of the following codes or one of the standard error codes (OLEUI_ERR_*)
 *              defined in OLE2UI.H, indicating success or error:
 *              OLEUI_OK                           User selected OK
 *              OLEUI_CANCEL                       User cancelled the dialog
 *              OLEUI_IOERR_SRCDATAOBJECTINVALID   lpSrcDataObject field of OLEUIPASTESPECIAL invalid
 *              OLEUI_IOERR_ARRPASTEENTRIESINVALID arrPasteEntries field of OLEUIPASTESPECIAL invalid
 *              OLEUI_IOERR_ARRLINKTYPESINVALID    arrLinkTypes field of OLEUIPASTESPECIAL invalid
 *              OLEUI_PSERR_CLIPBOARDCHANGED       Clipboard contents changed while dialog was up
 */

STDAPI_(UINT) OleUIPasteSpecial(LPOLEUIPASTESPECIAL lpPS)
{
   UINT        uRet;
   HGLOBAL     hMemDlg=NULL;

   uRet=UStandardValidation((LPOLEUISTANDARD)lpPS, sizeof(OLEUIPASTESPECIAL)
      , &hMemDlg);

   if (uRet != OLEUI_SUCCESS)
      return uRet;

   //Validate PasteSpecial specific fields
   if (NULL == lpPS->lpSrcDataObj || IsBadReadPtr(lpPS->lpSrcDataObj,  sizeof(IDataObject)))
      uRet = OLEUI_IOERR_SRCDATAOBJECTINVALID;
   if (NULL == lpPS->arrPasteEntries || IsBadReadPtr(lpPS->arrPasteEntries,  sizeof(OLEUIPASTEENTRY)))
      uRet = OLEUI_IOERR_ARRPASTEENTRIESINVALID;
   if (NULL != lpPS->arrLinkTypes && IsBadReadPtr(lpPS->arrLinkTypes,  sizeof(UINT)))
      uRet = OLEUI_IOERR_ARRLINKTYPESINVALID;

   if (0!=lpPS->cClsidExclude)
      {
      if (NULL!=lpPS->lpClsidExclude && IsBadReadPtr(lpPS->lpClsidExclude
         , lpPS->cClsidExclude*sizeof(CLSID)))
      uRet=OLEUI_IOERR_LPCLSIDEXCLUDEINVALID;
      }

   if (uRet >= OLEUI_ERR_STANDARDMIN)
   {
      if (NULL != hMemDlg)
         FreeResource(hMemDlg);
      return uRet;
   }

   //Now that we've validated everything, we can invoke the dialog.
   uRet = UStandardInvocation(PasteSpecialDialogProc, (LPOLEUISTANDARD)lpPS
      , hMemDlg, MAKEINTRESOURCE(IDD_PASTESPECIAL));

   /*
   * IF YOU ARE CREATING ANYTHING BASED ON THE RESULTS, DO IT HERE.
   */

   return uRet;
}


/*
 * PasteSpecialDialogProc
 *
 * Purpose:
 *  Implements the OLE Paste Special dialog as invoked through the
 *  OleUIPasteSpecial function.
 *
 * Parameters:
 *  Standard
 *
 * Return Value:
 *  Standard
 */

BOOL CALLBACK EXPORT PasteSpecialDialogProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
   LPOLEUIPASTESPECIAL     lpOPS;
   LPPASTESPECIAL          lpPS;
   BOOL                    fHook=FALSE;
   HCURSOR                 hCursorOld;

   //Declare Win16/Win32 compatible WM_COMMAND parameters.
   COMMANDPARAMS(wID, wCode, hWndMsg);

   //This will fail under WM_INITDIALOG, where we allocate it.
   lpPS=(LPPASTESPECIAL)LpvStandardEntry(hDlg, iMsg, wParam, lParam, &fHook);

   //If the hook processed the message, we're done.
   if (0!=fHook)
      return fHook;

   // Process help message from Change Icon
   if (iMsg == uMsgHelp)
   {
      PostMessage(lpPS->lpOPS->hWndOwner, uMsgHelp, wParam, lParam);
      return FALSE;
   }

   //Process the temination message
   if (iMsg==uMsgEndDialog)
   {
      HWND    hwndNextViewer;

      // Free the icon/icon-title metafile corresponding to Paste/PasteList option which is not selected
      if (lpPS->fLink)
         OleUIMetafilePictIconFree(lpPS->hMetaPictOD);
      else OleUIMetafilePictIconFree(lpPS->hMetaPictLSD);

      // Free data associated with each list box entry
      FreeListData(GetDlgItem(hDlg, ID_PS_PASTELIST));
      FreeListData(GetDlgItem(hDlg, ID_PS_PASTELINKLIST));

      //Free any specific allocations before calling StandardCleanup
      if (lpPS->hObjDesc) GlobalFree(lpPS->hObjDesc);
      if (lpPS->hLinkSrcDesc) GlobalFree(lpPS->hLinkSrcDesc);
      if (lpPS->hBuff) GlobalFree(lpPS->hBuff);

      // Change the clipboard notification chain
      hwndNextViewer = GetProp(hDlg, NEXTCBVIEWER);
      if (hwndNextViewer != HWND_BROADCAST)
      {
         SetProp(hDlg, NEXTCBVIEWER, HWND_BROADCAST);
         ChangeClipboardChain(hDlg, hwndNextViewer);
      }
      RemoveProp(hDlg, NEXTCBVIEWER);

      StandardCleanup(lpPS, hDlg);
      EndDialog(hDlg, wParam);
      return TRUE;
   }

   switch (iMsg)
   {
      case WM_INITDIALOG:
         hCursorOld = HourGlassOn();
         FPasteSpecialInit(hDlg, wParam, lParam);
         HourGlassOff(hCursorOld);
         return FALSE;

      case WM_DRAWCLIPBOARD:
      {
         HWND    hwndNextViewer = GetProp(hDlg, NEXTCBVIEWER);
         HWND    hDlg_ChgIcon;

         if (hwndNextViewer == HWND_BROADCAST)
            break;

         if (hwndNextViewer)
         {
            SendMessage(hwndNextViewer, iMsg, wParam, lParam);
            // Refresh next viewer in case it got modified
            //    by the SendMessage() (likely if multiple
            //    PasteSpecial dialogs are up simultaneously)
            hwndNextViewer = GetProp(hDlg, NEXTCBVIEWER);
         }
         SetProp(hDlg, NEXTCBVIEWER, HWND_BROADCAST);
         ChangeClipboardChain(hDlg, hwndNextViewer);

         /* OLE2NOTE: if the ChangeIcon dialog is currently up, then
         **    we need to defer bringing down PasteSpecial dialog
         **    until after ChangeIcon dialog returns. if the
         **    ChangeIcon dialog is NOT up, then we can bring down
         **    the PasteSpecial dialog immediately.
         */
         if ((hDlg_ChgIcon=(HWND)GetProp(hDlg,PROP_HWND_CHGICONDLG))!=NULL)
         {
            // ChangeIcon dialog is UP
            lpPS->fClipboardChanged = TRUE;
         } else {
            // ChangeIcon dialog is NOT up

            //  Free icon and icon title metafile
            SendDlgItemMessage(
                  hDlg, ID_PS_ICONDISPLAY, IBXM_IMAGEFREE, 0, 0L);

            SendMessage(
                  hDlg, uMsgEndDialog, OLEUI_PSERR_CLIPBOARDCHANGED,0L);
         }
         break;
      }

      case WM_CHANGECBCHAIN:
      {
         HWND    hwndNextViewer = GetProp(hDlg, NEXTCBVIEWER);

         if (wParam == (WORD)hwndNextViewer)
            SetProp(hDlg, NEXTCBVIEWER, (hwndNextViewer = (HWND)LOWORD(lParam)));
         else if (hwndNextViewer && hwndNextViewer != HWND_BROADCAST)
            SendMessage(hwndNextViewer, iMsg, wParam, lParam);
         break;
      }

      case WM_COMMAND:
         switch (wID)
         {
            case ID_PS_PASTE:
               FTogglePasteType(hDlg, lpPS, PSF_SELECTPASTE);
               break;

            case ID_PS_PASTELINK:
               FTogglePasteType(hDlg, lpPS, PSF_SELECTPASTELINK);
               break;

            case ID_PS_DISPLAYLIST:
               switch (wCode)
               {
                  case LBN_SELCHANGE:
                     ChangeListSelection(hDlg, lpPS, hWndMsg);
                     break;

                  case LBN_DBLCLK:
                     // Same as pressing OK
                     SendCommand(hDlg, IDOK, BN_CLICKED, hWndMsg);
                     break;
               }
               break;

            case ID_PS_DISPLAYASICON:
               ToggleDisplayAsIcon(hDlg, lpPS);
               break;

            case ID_PS_CHANGEICON:
               ChangeIcon(hDlg, lpPS);
               if (lpPS->fClipboardChanged) {
                  // Free icon and icon title metafile
                  SendDlgItemMessage(
                        hDlg, ID_PS_ICONDISPLAY, IBXM_IMAGEFREE,0,0L);
                  SendMessage(
                        hDlg, uMsgEndDialog,
                        OLEUI_PSERR_CLIPBOARDCHANGED, 0L);
               }
               break;

            case IDOK:
            {
               BOOL fDestAspectIcon =
                     ((lpPS->dwFlags & PSF_CHECKDISPLAYASICON) ?
                           TRUE : FALSE);
               lpOPS = lpPS->lpOPS;
               // Return current flags
               lpOPS->dwFlags = lpPS->dwFlags;
               // Return index of arrPasteEntries[] corresponding to format selected by user
               lpOPS->nSelectedIndex = lpPS->nSelectedIndex;
               // Return if user selected Paste or PasteLink
               lpOPS->fLink = lpPS->fLink;

               /* if user selected same ASPECT as displayed in the
               **    source, then sizel passed in the
               **    ObjectDescriptor/LinkSrcDescriptor is
               **    applicable. otherwise, the sizel does not apply.
               */
               if (lpPS->fLink) {
                  if (lpPS->fSrcAspectIconLSD == fDestAspectIcon)
                     lpOPS->sizel = lpPS->sizelLSD;
                  else
                     lpOPS->sizel.cx = lpOPS->sizel.cy = 0;
               } else {
                  if (lpPS->fSrcAspectIconOD == fDestAspectIcon)
                     lpOPS->sizel = lpPS->sizelOD;
                  else
                     lpOPS->sizel.cx = lpOPS->sizel.cy = 0;
               }
               // Return metafile with icon and icon title that the user selected
               lpOPS->hMetaPict=(HGLOBAL)SendDlgItemMessage(hDlg, ID_PS_ICONDISPLAY,
                                       IBXM_IMAGEGET, 0, 0L);
               SendMessage(hDlg, uMsgEndDialog, OLEUI_OK, 0L);
               break;
            }
            case IDCANCEL:
               // Free icon and icon title metafile
               SendDlgItemMessage(
                     hDlg, ID_PS_ICONDISPLAY, IBXM_IMAGEFREE, 0, 0L);
               SendMessage(hDlg, uMsgEndDialog, OLEUI_CANCEL, 0L);
               break;

            case ID_OLEUIHELP:
               PostMessage(lpPS->lpOPS->hWndOwner, uMsgHelp,
                  (WPARAM)hDlg, MAKELPARAM(IDD_PASTESPECIAL, 0));
               break;
         }
         break;
   }
   return FALSE;
}


/*
 * FPasteSpecialInit
 *
 * Purpose:
 *  WM_INITIDIALOG handler for the Paste Special dialog box.
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  wParam          WPARAM of the message
 *  lParam          LPARAM of the message
 *
 * Return Value:
 *  BOOL            Value to return for WM_INITDIALOG.
 */

BOOL FPasteSpecialInit(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
   LPPASTESPECIAL              lpPS;
   LPOLEUIPASTESPECIAL         lpOPS;
   HFONT                       hFont;
   BOOL                        fPasteAvailable, fPasteLinkAvailable;
   STGMEDIUM                   medium;
   LPOBJECTDESCRIPTOR          lpOD;
   LPLINKSRCDESCRIPTOR         lpLSD;
   int                         n;
   CLIPFORMAT                  cfFormat;
   char                        szAnsiStr[256];
   LPSTR                       lpszAnsiStr;

   // Copy the structure at lParam into our instance memory.
   lpPS = (LPPASTESPECIAL)LpvStandardInit(hDlg, sizeof(PASTESPECIAL), TRUE, &hFont);

   // PvStandardInit sent a termination to us already.
   if (NULL == lpPS)
      return FALSE;

   lpOPS=(LPOLEUIPASTESPECIAL)lParam;

   // Copy other information from lpOPS that we might modify.
   lpPS->lpOPS = lpOPS;
   lpPS->dwFlags = lpOPS->dwFlags;

   // Initialize user selections in the Paste and PasteLink listboxes
   lpPS->nPasteListCurSel = 0;
   lpPS->nPasteLinkListCurSel = 0;

   // If we got a font, send it to the necessary controls.
   if (NULL!=hFont)
   {
      SendDlgItemMessage(hDlg, ID_PS_SOURCETEXT, WM_SETFONT, (WPARAM)hFont, 0L);
      SendDlgItemMessage(hDlg, ID_PS_RESULTTEXT, WM_SETFONT, (WPARAM)hFont, 0L);
   }

   // Hide the help button if required
   if (!(lpPS->lpOPS->dwFlags & PSF_SHOWHELP))
      StandardShowDlgItem(hDlg, ID_OLEUIHELP, SW_HIDE);

   // Hide all DisplayAsIcon related controls if it should be disabled
   if ( lpPS->dwFlags & PSF_DISABLEDISPLAYASICON ) {
        StandardShowDlgItem(hDlg, ID_PS_DISPLAYASICON, SW_HIDE);
        StandardShowDlgItem(hDlg, ID_PS_CHANGEICON, SW_HIDE);
        StandardShowDlgItem(hDlg, ID_PS_ICONDISPLAY, SW_HIDE);
   }

   // PSF_CHECKDISPLAYASICON is an OUT flag. Clear it if has been set on the way in.
   lpPS->dwFlags = lpPS->dwFlags & ~PSF_CHECKDISPLAYASICON;

   //  Change the caption if required
   if (NULL != lpOPS->lpszCaption) {
      W2A (lpOPS->lpszCaption, szAnsiStr, 256);
      SetWindowText(hDlg, szAnsiStr);
   }

   // Load 'Unknown Source' and 'Unknown Type' strings
   n = LoadString(ghInst, (UINT)IDS_PSUNKNOWNTYPE, szAnsiStr, PS_UNKNOWNSTRLEN);
   A2W (szAnsiStr, lpPS->szUnknownType, PS_UNKNOWNSTRLEN);
   if (n) {
      n = LoadString(ghInst, (UINT)IDS_PSUNKNOWNSRC, szAnsiStr, PS_UNKNOWNSTRLEN);
      A2W (szAnsiStr, lpPS->szUnknownSource, PS_UNKNOWNSTRLEN);
   }
   if (!n)
   {
      PostMessage(hDlg, uMsgEndDialog, OLEUI_ERR_LOADSTRING, 0L);
      return FALSE;
   }
   lpPS->szAppName[0]='\0';

   // GetData CF_OBJECTDESCRIPTOR. If the object on the clipboard in an OLE1 object (offering CF_OWNERLINK)
   // or has been copied to clipboard by FileMaager (offering CF_FILENAME), an OBJECTDESCRIPTOR will be
   // created will be created from CF_OWNERLINK or CF_FILENAME. See OBJECTDESCRIPTOR for more info.

   if (lpPS->hObjDesc = OleStdFillObjectDescriptorFromData(lpOPS->lpSrcDataObj, &medium, &cfFormat))
   {
      lpOD = GlobalLock(lpPS->hObjDesc);

      // Get FullUserTypeName, SourceOfCopy and CLSID
      if (lpOD->dwFullUserTypeName)
         lpPS->szFullUserTypeNameOD = (LPOLESTR)
                                  ((LPSTR)lpOD + lpOD->dwFullUserTypeName) ;
         else lpPS->szFullUserTypeNameOD = lpPS->szUnknownType;

      if (lpOD->dwSrcOfCopy)
      {
         lpPS->szSourceOfDataOD = (LPOLESTR)((LPSTR)lpOD+lpOD->dwSrcOfCopy);
         // If CF_FILENAME was offered, source of copy is a path name. Fit the path to the
         // static control that will display it.
         if (cfFormat == cfFileName) {
            W2A (lpPS->szSourceOfDataOD, szAnsiStr, 256);
            lpszAnsiStr = ChopText(GetDlgItem(hDlg, ID_PS_SOURCETEXT), 0, szAnsiStr);
            A2W (lpszAnsiStr, lpPS->szSourceOfDataOD, 256);
         }
      }
      else lpPS->szSourceOfDataOD = lpPS->szUnknownSource;

      lpPS->clsidOD = lpOD->clsid;
      lpPS->sizelOD = lpOD->sizel;

      // Does source specify DVASPECT_ICON?
      if (lpOD->dwDrawAspect & DVASPECT_ICON)
         lpPS->fSrcAspectIconOD = TRUE;
      else lpPS->fSrcAspectIconOD = FALSE;

      // Does source specify OLEMISC_ONLYICONIC?
      if (lpOD->dwStatus & OLEMISC_ONLYICONIC)
         lpPS->fSrcOnlyIconicOD = TRUE;
      else lpPS->fSrcOnlyIconicOD = FALSE;

      // Get application name of source from auxusertype3 in the registration database
      if (0==OleStdGetAuxUserType(&lpPS->clsidOD, 3, lpPS->szAppName, OLEUI_CCHKEYMAX, NULL))
      {
          // Use "the application which created it" as the name of the application
          if (0==LoadString(ghInst, (UINT)IDS_PSUNKNOWNAPP, szAnsiStr, PS_UNKNOWNSTRLEN))
          {
             A2W (szAnsiStr, lpPS->szAppName, PS_UNKNOWNSTRLEN);
             PostMessage(hDlg, uMsgEndDialog, OLEUI_ERR_LOADSTRING, 0L);
             return FALSE;
          }
      }

      // Retrieve an icon from the object
      if (lpPS->fSrcAspectIconOD)
      {
         lpPS->hMetaPictOD = OleStdGetData(
            lpOPS->lpSrcDataObj,
            CF_METAFILEPICT,
            NULL,
            DVASPECT_ICON,
            &medium
         );

      }
      // If object does not offer icon, obtain it from the CLSID
      if (NULL == lpPS->hMetaPictOD)
      {
         lpPS->hMetaPictOD = OleGetIconOfClass(&lpPS->clsidOD, NULL,
               TRUE);   // Use the short user type name (auxusertype3)
      }
   }

   // Does object offer CF_LINKSRCDESCRIPTOR?
   if (lpPS->hLinkSrcDesc = OleStdGetData(
         lpOPS->lpSrcDataObj,
         (CLIPFORMAT)cfLinkSrcDescriptor,
         NULL,
         DVASPECT_CONTENT,
         &medium))
   {
      // Get FullUserTypeName, SourceOfCopy and CLSID
      lpLSD = GlobalLock(lpPS->hLinkSrcDesc);
      if (lpLSD->dwFullUserTypeName)
         lpPS->szFullUserTypeNameLSD =
                   (LPOLESTR) ((LPSTR)lpLSD + lpLSD->dwFullUserTypeName);
      else lpPS->szFullUserTypeNameLSD = lpPS->szUnknownType;

      if (lpLSD->dwSrcOfCopy)
         lpPS->szSourceOfDataLSD = (LPOLESTR)((LPSTR)lpLSD+lpLSD->dwSrcOfCopy);
      else lpPS->szSourceOfDataLSD = lpPS->szUnknownSource;

      // if no ObjectDescriptor, then use LinkSourceDescriptor source string
      if (!lpPS->hObjDesc)
         lpPS->szSourceOfDataOD = lpPS->szSourceOfDataLSD;

      lpPS->clsidLSD = lpLSD->clsid;
      lpPS->sizelLSD = lpLSD->sizel;

      // Does source specify DVASPECT_ICON?
      if (lpLSD->dwDrawAspect & DVASPECT_ICON)
         lpPS->fSrcAspectIconLSD = TRUE;
      else lpPS->fSrcAspectIconLSD = FALSE;

      // Does source specify OLEMISC_ONLYICONIC?
      if (lpLSD->dwStatus & OLEMISC_ONLYICONIC)
         lpPS->fSrcOnlyIconicLSD = TRUE;
      else lpPS->fSrcOnlyIconicLSD = FALSE;

      // Retrieve an icon from the object
      if (lpPS->fSrcAspectIconLSD)
      {
         lpPS->hMetaPictLSD = OleStdGetData(
            lpOPS->lpSrcDataObj,
            CF_METAFILEPICT,
            NULL,
            DVASPECT_ICON,
            &medium
         );

      }
      // If object does not offer icon, obtain it from the CLSID
      if (NULL == lpPS->hMetaPictLSD)
      {
         char szLabel[OLEUI_CCHLABELMAX];
         HWND hIconWnd;
         RECT IconRect;
         int  nWidth;
         LPSTR lpszLabel;
         OLECHAR szUniLabel[256];

         hIconWnd = GetDlgItem(hDlg, ID_PS_ICONDISPLAY);

         GetClientRect(hIconWnd, &IconRect);

         nWidth = ((IconRect.right-IconRect.left) * 3) / 2;   // width is 1.5 times width of iconbox

         W2A(lpPS->szSourceOfDataLSD, szLabel, sizeof(szLabel));
         szLabel[sizeof(szLabel)-1] = '\0';

         lpszLabel = ChopText(hIconWnd, nWidth, (LPSTR)szLabel);

         A2W (lpszLabel, szUniLabel, 256);
         lpPS->hMetaPictLSD = OleGetIconOfClass(&lpPS->clsidLSD,
               szUniLabel,       /* use chopped source string as label */
               FALSE            /* not applicable */
         );
      }
   }
   else if (lpPS->hObjDesc)     // Does not offer CF_LINKSRCDESCRIPTOR but offers CF_OBJECTDESCRIPTOR
   {
      // Copy the values of OBJECTDESCRIPTOR
      lpPS->szFullUserTypeNameLSD = lpPS->szFullUserTypeNameOD;
      lpPS->szSourceOfDataLSD = lpPS->szSourceOfDataOD;
      lpPS->clsidLSD = lpPS->clsidOD;
      lpPS->sizelLSD = lpPS->sizelOD;
      lpPS->fSrcAspectIconLSD = lpPS->fSrcAspectIconOD;
      lpPS->fSrcOnlyIconicLSD = lpPS->fSrcOnlyIconicOD;

      // Don't copy the hMetaPict; instead get a separate copy
      if (lpPS->fSrcAspectIconLSD)
      {
         lpPS->hMetaPictLSD = OleStdGetData(
            lpOPS->lpSrcDataObj,
            CF_METAFILEPICT,
            NULL,
            DVASPECT_ICON,
            &medium
         );
      }
      if (NULL == lpPS->hMetaPictLSD)
      {
         char szLabel[OLEUI_CCHLABELMAX];
         HWND hIconWnd;
         RECT IconRect;
         int  nWidth;
         LPSTR lpszLabel;
         OLECHAR szUniLabel[256];

         hIconWnd = GetDlgItem(hDlg, ID_PS_ICONDISPLAY);

         GetClientRect(hIconWnd, &IconRect);

         nWidth = ((IconRect.right-IconRect.left) * 3) / 2;   // width is 1.5 times width of iconbox

         W2A(lpPS->szSourceOfDataLSD, szLabel, sizeof(szLabel));
         szLabel[sizeof(szLabel)-1] = '\0';

         lpszLabel = ChopText(hIconWnd, nWidth, (LPSTR)szLabel);

         A2W (lpszLabel, szUniLabel, 256);
         lpPS->hMetaPictLSD = OleGetIconOfClass(&lpPS->clsidLSD,
               szUniLabel,   /* Use chopped source string as label */
               FALSE        /* Not applicable */
         );
      }
   }

   // Not an OLE object
   if (lpPS->hObjDesc == NULL && lpPS->hLinkSrcDesc == NULL)
   {
       lpPS->szFullUserTypeNameLSD = lpPS->szFullUserTypeNameOD = lpPS->szUnknownType;
       lpPS->szSourceOfDataLSD = lpPS->szSourceOfDataOD = lpPS->szUnknownSource;
       lpPS->hMetaPictLSD = lpPS->hMetaPictOD = NULL;
   }

   // Allocate scratch memory to construct item names in the paste and pastelink listboxes
   lpPS->hBuff = AllocateScratchMem(lpPS);
   if (lpPS->hBuff == NULL)
   {
      PostMessage(hDlg, uMsgEndDialog, OLEUI_ERR_GLOBALMEMALLOC, 0L);
      return FALSE;
   }

   // Select the Paste Link Button if specified. Otherwise select
   //      Paste Button by default
   if (lpPS->dwFlags & PSF_SELECTPASTELINK)
      lpPS->dwFlags = (lpPS->dwFlags & ~PSF_SELECTPASTE) | PSF_SELECTPASTELINK;
   else
      lpPS->dwFlags =(lpPS->dwFlags & ~PSF_SELECTPASTELINK) | PSF_SELECTPASTE;

   // Mark which PasteEntry formats are available from source data object
   OleStdMarkPasteEntryList(
         lpOPS->lpSrcDataObj,lpOPS->arrPasteEntries,lpOPS->cPasteEntries);

   // Check if items are available to be pasted
   fPasteAvailable = FFillPasteList(hDlg, lpPS);
   if (!fPasteAvailable)
   {
      lpPS->dwFlags &= ~PSF_SELECTPASTE;
      EnableWindow(GetDlgItem(hDlg, ID_PS_PASTE), FALSE);
   }

   // Check if items are available to be paste-linked
   fPasteLinkAvailable = FFillPasteLinkList(hDlg, lpPS);
   if (!fPasteLinkAvailable)
   {
      lpPS->dwFlags &= ~PSF_SELECTPASTELINK;
      EnableWindow(GetDlgItem(hDlg, ID_PS_PASTELINK), FALSE);
   }

   // If one of Paste or PasteLink is disabled, select the other one
   //    regardless of what the input flags say
   if (fPasteAvailable && !fPasteLinkAvailable)
      lpPS->dwFlags |= PSF_SELECTPASTE;
   if (fPasteLinkAvailable && !fPasteAvailable)
      lpPS->dwFlags |= PSF_SELECTPASTELINK;

   if (lpPS->dwFlags & PSF_SELECTPASTE)
   {
      // FTogglePaste will set the PSF_SELECTPASTE flag, so clear it.
      lpPS->dwFlags &= ~PSF_SELECTPASTE;
      CheckRadioButton(hDlg, ID_PS_PASTE, ID_PS_PASTELINK, ID_PS_PASTE);
      FTogglePasteType(hDlg, lpPS, PSF_SELECTPASTE);
   }
   else if (lpPS->dwFlags & PSF_SELECTPASTELINK)
   {
      // FTogglePaste will set the PSF_SELECTPASTELINK flag, so clear it.
      lpPS->dwFlags &= ~PSF_SELECTPASTELINK;
      CheckRadioButton(hDlg, ID_PS_PASTE, ID_PS_PASTELINK, ID_PS_PASTELINK);
      FTogglePasteType(hDlg, lpPS, PSF_SELECTPASTELINK);
   }
   else  // Items are not available to be be Pasted or Paste-Linked
   {
      // Enable or disable DisplayAsIcon and set the result text and image
      EnableDisplayAsIcon(hDlg, lpPS);
      SetPasteSpecialHelpResults(hDlg, lpPS);
   }

   // Give initial focus to the list box
   SetFocus(GetDlgItem(hDlg, ID_PS_DISPLAYLIST));

   // Set property to handle clipboard change notifications
   SetProp(hDlg, NEXTCBVIEWER, HWND_BROADCAST);
   SetProp(hDlg, NEXTCBVIEWER, SetClipboardViewer(hDlg));

   lpPS->fClipboardChanged = FALSE;

   /*
    * PERFORM OTHER INITIALIZATION HERE.
    */

   // Call the hook with lCustData in lParam
   UStandardHook(lpPS, hDlg, WM_INITDIALOG, wParam, lpOPS->lCustData);
   return TRUE;
}

/*
 * FTogglePasteType
 *
 * Purpose:
 *  Toggles between Paste and Paste Link. The Paste list and PasteLink
 *  list are always invisible. The Display List is filled from either
 *  the Paste list or the PasteLink list depending on which Paste radio
 *  button is selected.
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  lpPS             Paste Special Dialog Structure
 *  dwOption        Paste or PasteSpecial option
 *
 * Return Value:
 *  BOOL            Returns TRUE if the option has already been selected.
 *                  Otherwise the option is selected and FALSE is returned
 */

BOOL FTogglePasteType(HWND hDlg, LPPASTESPECIAL lpPS, DWORD dwOption)
{
   DWORD dwTemp;
   HWND hList, hListDisplay;
   DWORD dwData;
   int i, nItems;
   LPSTR lpsz;
   char  szAnsiStr[256];

   // Skip all this if the button is already selected
   if (lpPS->dwFlags & dwOption)
      return TRUE;

   dwTemp = PSF_SELECTPASTE | PSF_SELECTPASTELINK;
   lpPS->dwFlags = (lpPS->dwFlags & ~dwTemp) | dwOption;

   // Hide IconDisplay. This prevents flashing if the icon display is changed
   StandardShowDlgItem(hDlg, ID_PS_ICONDISPLAY, SW_HIDE);

   hListDisplay = GetDlgItem(hDlg, ID_PS_DISPLAYLIST);

   // If Paste was selected
   if (lpPS->dwFlags & PSF_SELECTPASTE)
   {
      // Set the Source of the object in the clipboard
      W2A (lpPS->szSourceOfDataOD, szAnsiStr, 256);
      SetDlgItemText(hDlg, ID_PS_SOURCETEXT, szAnsiStr);

      // If an icon is available
      if (lpPS->hMetaPictOD)
         // Set the icon display
         SendDlgItemMessage(hDlg, ID_PS_ICONDISPLAY, IBXM_IMAGESET,
              (WPARAM)lpPS->hMetaPictOD, 0L);


      hList = GetDlgItem(hDlg, ID_PS_PASTELIST);
      // We are switching from PasteLink to Paste. Remember current selection
      //    in PasteLink list so it can be restored.
      lpPS->nPasteLinkListCurSel = (int)SendMessage(hListDisplay, LB_GETCURSEL, 0, 0L);
      if (lpPS->nPasteLinkListCurSel == LB_ERR)
         lpPS->nPasteLinkListCurSel = 0;
      // Remember if user selected Paste or PasteLink
      lpPS->fLink = FALSE;
   }
   else    // If PasteLink was selected
   {
      // Set the Source of the object in the clipboard
      W2A (lpPS->szSourceOfDataLSD, szAnsiStr, 256);
      SetDlgItemText(hDlg, ID_PS_SOURCETEXT, szAnsiStr);

      // If an icon is available
      if (lpPS->hMetaPictLSD)
         // Set the icon display
         SendDlgItemMessage(hDlg, ID_PS_ICONDISPLAY, IBXM_IMAGESET,
              (WPARAM)lpPS->hMetaPictLSD, 0L);


      hList = GetDlgItem(hDlg, ID_PS_PASTELINKLIST);
      // We are switching from Paste to PasteLink. Remember current selection
      //    in Paste list so it can be restored.
      lpPS->nPasteListCurSel = (int)SendMessage(hListDisplay, LB_GETCURSEL, 0, 0L);
      if (lpPS->nPasteListCurSel == LB_ERR)
         lpPS->nPasteListCurSel = 0;
      // Remember if user selected Paste or PasteLink
      lpPS->fLink = TRUE;
   }

   // Turn drawing off while the Display List is being filled
   SendMessage(hListDisplay, WM_SETREDRAW, (WPARAM)FALSE, 0L);

   // Move data to Display list box
   SendMessage(hListDisplay, LB_RESETCONTENT, 0, 0L);
   nItems = (int) SendMessage(hList, LB_GETCOUNT, 0, 0L);
   lpsz = (LPSTR)GlobalLock(lpPS->hBuff);
   for (i = 0; i < nItems; i++)
   {
      SendMessage(hList, LB_GETTEXT, (WPARAM)i, (LPARAM)lpsz);
      dwData = SendMessage(hList, LB_GETITEMDATA, (WPARAM)i, 0L);
      SendMessage(hListDisplay, LB_INSERTSTRING, (WPARAM)i, (LPARAM)lpsz);
      SendMessage(hListDisplay, LB_SETITEMDATA, (WPARAM)i, dwData);
   }
   GlobalUnlock(lpPS->hBuff);

   // Restore the selection in the Display List from user's last selection
   if (lpPS->dwFlags & PSF_SELECTPASTE)
      SendMessage(hListDisplay, LB_SETCURSEL, lpPS->nPasteListCurSel, 0L);
   else
      SendMessage(hListDisplay, LB_SETCURSEL, lpPS->nPasteLinkListCurSel, 0L);

   // Paint Display List
   SendMessage(hListDisplay, WM_SETREDRAW, (WPARAM)TRUE, 0L);
   InvalidateRect(hListDisplay, NULL, TRUE);
   UpdateWindow(hListDisplay);

   // Auto give the focus to the Display List
   SetFocus(hListDisplay);

   // Enable/Disable DisplayAsIcon and set the help result text and bitmap corresponding to
   //    the current selection
   ChangeListSelection(hDlg, lpPS, hListDisplay);

   return FALSE;
}


/*
 * ChangeListSelection
 *
 * Purpose:
 *  When the user changes the selection in the list, DisplayAsIcon is enabled or disabled,
 *  Result text and bitmap are updated and the index of the arrPasteEntries[] corresponding
 *  to the current format selection is saved.
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  lpPS             Paste Special Dialog Structure
 *  hList           HWND of the List
 *
 * Return Value:
 *  No return value
 */

void ChangeListSelection(HWND hDlg, LPPASTESPECIAL lpPS, HWND hList)
{
   LPPASTELISTITEMDATA lpItemData;
   int nCurSel;

   EnableDisplayAsIcon(hDlg, lpPS);
   SetPasteSpecialHelpResults(hDlg, lpPS);

   // Remember index of arrPasteEntries[] corresponding to the current selection
   nCurSel = (int)SendMessage(hList, LB_GETCURSEL, 0, 0L);
   if (nCurSel == LB_ERR) return;
   lpItemData = (LPPASTELISTITEMDATA) SendMessage(hList, LB_GETITEMDATA,
            (WPARAM)nCurSel, 0L);
   if ((LRESULT)lpItemData == LB_ERR) return;
   lpPS->nSelectedIndex = lpItemData->nPasteEntriesIndex;
}

/*
 * EnableDisplayAsIcon
 *
 * Purpose:
 *  Enable or disable the DisplayAsIcon button depending on whether
 *  the current selection can be displayed as an icon or not. The following table describes
 *  the state of DisplayAsIcon. The calling application is termed CONTAINER, the source
 *  of data on the clipboard is termed SOURCE.
 *  Y = Yes; N = No; Blank = State does not matter;
 * =====================================================================
 * SOURCE          SOURCE             CONTAINER             DisplayAsIcon
 * specifies       specifies          specifies             Initial State
 * DVASPECT_ICON   OLEMISC_ONLYICONIC OLEUIPASTE_ENABLEICON
 *
 *                                    N                     Unchecked&Disabled
 *                 Y                  Y                     Checked&Disabled
 * Y               N                  Y                     Checked&Enabled
 * N               N                  Y                     Unchecked&Enabled
 * =====================================================================
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  lpPS             Paste Special Dialog Structure
 *
 * Return Value:
 *  No return value
 */

void EnableDisplayAsIcon(HWND hDlg, LPPASTESPECIAL lpPS)
{
   int nIndex;
   BOOL fCntrEnableIcon;
   BOOL fSrcOnlyIconic = (lpPS->fLink) ? lpPS->fSrcOnlyIconicLSD : lpPS->fSrcOnlyIconicOD;
   BOOL fSrcAspectIcon = (lpPS->fLink) ? lpPS->fSrcAspectIconLSD : lpPS->fSrcAspectIconOD;
   HWND hList;
   LPPASTELISTITEMDATA lpItemData;
   HGLOBAL hMetaPict = (lpPS->fLink) ? lpPS->hMetaPictLSD : lpPS->hMetaPictOD;

   hList = GetDlgItem(hDlg, ID_PS_DISPLAYLIST);

   // Get data corresponding to the current selection in the listbox
   nIndex = (int)SendMessage(hList, LB_GETCURSEL, 0, 0);
   if (nIndex != LB_ERR)
   {
      lpItemData = (LPPASTELISTITEMDATA) SendMessage(hList, LB_GETITEMDATA, (WPARAM)nIndex, 0L);
      if ((LRESULT)lpItemData != LB_ERR)
         fCntrEnableIcon = lpItemData->fCntrEnableIcon;
      else fCntrEnableIcon = FALSE;
   }
   else fCntrEnableIcon = FALSE;

   // If there is an icon available
   if (hMetaPict != NULL)
   {
      if (!fCntrEnableIcon)          // Does CONTAINER specify OLEUIPASTE_ENABLEICON?
      {
         // Uncheck & Disable DisplayAsIcon
         lpPS->dwFlags &= ~PSF_CHECKDISPLAYASICON;
         CheckDlgButton(hDlg, ID_PS_DISPLAYASICON, FALSE);
         EnableWindow(GetDlgItem(hDlg, ID_PS_DISPLAYASICON), FALSE);

         // Hide IconDisplay and ChangeIcon button
         StandardShowDlgItem(hDlg, ID_PS_ICONDISPLAY, SW_HIDE);
         StandardShowDlgItem(hDlg, ID_PS_CHANGEICON, SW_HIDE);
      }
      else if (fSrcOnlyIconic)       // Does SOURCE specify OLEMISC_ONLYICONIC?
      {
         // Check & Disable DisplayAsIcon
         lpPS->dwFlags |= PSF_CHECKDISPLAYASICON;
         CheckDlgButton(hDlg, ID_PS_DISPLAYASICON, TRUE);
         EnableWindow(GetDlgItem(hDlg, ID_PS_DISPLAYASICON), FALSE);

         // Show IconDisplay and ChangeIcon button
         StandardShowDlgItem(hDlg, ID_PS_ICONDISPLAY, SW_SHOWNORMAL);
         StandardShowDlgItem(hDlg, ID_PS_CHANGEICON, SW_SHOWNORMAL);
      }
      else if (fSrcAspectIcon)       // Does SOURCE specify DVASPECT_ICON?
      {
          // Check & Enable DisplayAsIcon
          lpPS->dwFlags |= PSF_CHECKDISPLAYASICON;
          CheckDlgButton(hDlg, ID_PS_DISPLAYASICON, TRUE);
          EnableWindow(GetDlgItem(hDlg, ID_PS_DISPLAYASICON), TRUE);

          // Show IconDisplay and ChangeIcon button
          StandardShowDlgItem(hDlg, ID_PS_ICONDISPLAY, SW_SHOWNORMAL);
          StandardShowDlgItem(hDlg, ID_PS_CHANGEICON, SW_SHOWNORMAL);
      }
      else
      {
          //Uncheck and Enable DisplayAsIcon
          lpPS->dwFlags &= ~PSF_CHECKDISPLAYASICON;
          CheckDlgButton(hDlg, ID_PS_DISPLAYASICON, FALSE);
          EnableWindow(GetDlgItem(hDlg, ID_PS_DISPLAYASICON), TRUE);

          // Hide IconDisplay and ChangeIcon button
          StandardShowDlgItem(hDlg, ID_PS_ICONDISPLAY, SW_HIDE);
          StandardShowDlgItem(hDlg, ID_PS_CHANGEICON, SW_HIDE);

      }
   }
   else  // No icon available
   {
      // Unchecked & Disabled
      lpPS->dwFlags &= ~PSF_CHECKDISPLAYASICON;
      CheckDlgButton(hDlg, ID_PS_DISPLAYASICON, FALSE);
      EnableWindow(GetDlgItem(hDlg, ID_PS_DISPLAYASICON), FALSE);

      // Hide IconDisplay and ChangeIcon button
      StandardShowDlgItem(hDlg, ID_PS_ICONDISPLAY, SW_HIDE);
      StandardShowDlgItem(hDlg, ID_PS_CHANGEICON, SW_HIDE);
   }
}

/*
 * ToggleDisplayAsIcon
 *
 * Purpose:
 *  Toggles the DisplayAsIcon button. Hides or shows the Icon Display and
 *  the ChangeIcon button and changes the help result text and bitmap.
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  lpPS             Paste Special Dialog Structure
 *
 * Return Value:
 *  None
 *
 */

void ToggleDisplayAsIcon(HWND hDlg, LPPASTESPECIAL lpPS)
{
   BOOL fCheck;
   int i;

   fCheck = IsDlgButtonChecked(hDlg, ID_PS_DISPLAYASICON);

   if (fCheck)
      lpPS->dwFlags |= PSF_CHECKDISPLAYASICON;
   else lpPS->dwFlags &= ~PSF_CHECKDISPLAYASICON;

   // Set the help result text and bitmap
   SetPasteSpecialHelpResults(hDlg, lpPS);

   // Show or hide the Icon Display and ChangeIcon button depending
   // on the check state
   i = (fCheck) ? SW_SHOWNORMAL : SW_HIDE;
   StandardShowDlgItem(hDlg, ID_PS_ICONDISPLAY, i);
   StandardShowDlgItem(hDlg, ID_PS_CHANGEICON, i);
}

/*
 * ChangeIcon
 *
 * Purpose:
 *  Brings up the ChangeIcon dialog which allows the user to change
 *  the icon and label.
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  lpPS             Paste Special Dialog Structure
 *
 * Return Value:
 *  None
 *
 */

void ChangeIcon(HWND hDlg, LPPASTESPECIAL lpPS)
{
   OLEUICHANGEICON ci;
   UINT uRet;
   CLSID   clsid     = (lpPS->fLink) ? lpPS->clsidLSD : lpPS->clsidOD;

   //Initialize the structure
   _fmemset((LPOLEUICHANGEICON)&ci, 0, sizeof(ci));

   ci.hMetaPict = (HGLOBAL)SendDlgItemMessage(hDlg, ID_PS_ICONDISPLAY, IBXM_IMAGEGET, 0, 0L);
   ci.cbStruct = sizeof(ci);
   ci.hWndOwner = hDlg;
   ci.clsid = clsid;
   ci.dwFlags  = CIF_SELECTCURRENT;

   // Only show help in the ChangeIcon dialog if we're showing it in this dialog.
   if (lpPS->dwFlags & PSF_SHOWHELP)
      ci.dwFlags |= CIF_SHOWHELP;

   // Let the hook in to customize Change Icon if desired.
   uRet = UStandardHook(lpPS, hDlg, uMsgChangeIcon, 0, (LONG)&ci);

   if (0 == uRet)
      uRet=(UINT)(OLEUI_OK==OleUIChangeIcon(&ci));

   // Update the display if necessary.
   if (0!=uRet)
   {
      /*
      * OleUIChangeIcon will have already freed our
      * current hMetaPict that we passed in when OK is
      * pressed in that dialog.  So we use 0L as lParam
      * here so the IconBox doesn't try to free the
      * metafilepict again.
      */
      SendDlgItemMessage(hDlg, ID_PS_ICONDISPLAY, IBXM_IMAGESET, (WPARAM)ci.hMetaPict, 0L);
      // Remember the new icon chosen by the user. Note that Paste and PasteLink have separate
      //    icons - changing one does not change the other.
      if (lpPS->fLink)
         lpPS->hMetaPictLSD = ci.hMetaPict;
      else lpPS->hMetaPictOD = ci.hMetaPict;
   }
}

/*
 *SetPasteSpecialHelpResults
 *
 * Purpose:
 *  Sets the help result text and bitmap according to the current
 *  list selection. The following state table indicates which ResultText
 *  and ResultImage are selected. If %s in the lpstrFormatName is present,
 *  it is assumed that an object is being pasted/paste-linked, otherwise it
 *  is assumed that data is being pasted/paste-linked.
 *  Y = Yes; N = No; Blank = State does not matter;
 *  The numbers in the the ResultText and ResultImage columns refer to the table
 *  entries that follow.
 * =====================================================================
 * Paste/       lpstrFormatName in                DisplayAsIcon Result      Result
 * PasteLink    arrPasteEntry[]contains %s        checked       Text        Image
 *              (Is Object == Y, Is Data == N)
 * Paste        N                                               1           1
 * Paste        Y                                 N             2           2
 * Paste        Y                                 Y             3           3
 * PasteLink    N                                               4           4
 * PasteLink    Y                                 N             5           4
 * PasteLink    Y                                 Y             6           5
 * =====================================================================
 * Result Text:
 *
 * 1. "Inserts the contents of the Clipboard into your document as <native type name,
 *     and optionally an additional help sentence>"
 * 2. "Inserts the contents of the Clipboard into your document so that you may
 *     activate it using <object app name>"
 * 3. "Inserts the contents of the Clipboard into your document so that you may
 *     activate it using <object app name>.  It will be displayed as an icon."
 * 4. "Inserts the contents of the Clipboard into your document as <native type name>.
 *     Paste Link creates a link to the source file so that changes to the source file
 *     will be reflected in your document."
 * 5. "Inserts a picture of the Clipboard contents into your document.  Paste Link
 *     creates a link to the source file so that changes to the source file will be
 *     reflected in your document."
 * 6. "Inserts an icon into your document which represents the Clipboard contents.
 *     Paste Link creates a link to the source file so that changes to the source file
 *     will be reflected in your document."
 * =====================================================================
 * Result Image:
 *
 * 1. Clipboard Image
 * 2. Paste image, non-iconic.
 * 3. Paste image, iconic.
 * 4. Paste Link image, non-iconic
 * 5. Paste Link image, iconic
 * ====================================================================
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  lpPS             Paste Special Dialog Structure
 *
 * Return Value:
 *  No return value
 */
void SetPasteSpecialHelpResults(HWND hDlg, LPPASTESPECIAL lpPS)
{
   LPSTR               psz1, psz2, psz3, psz4;
   UINT                i, iString, iImage, cch;
   int                 nPasteEntriesIndex;
   BOOL                fDisplayAsIcon;
   BOOL                fIsObject;
   HWND                hList;
   LPPASTELISTITEMDATA  lpItemData;
   LPOLEUIPASTESPECIAL lpOPS = lpPS->lpOPS;
   LPOLESTR    szFullUserTypeName = (lpPS->fLink) ?
               lpPS->szFullUserTypeNameLSD : lpPS->szFullUserTypeNameOD;
   LPOLESTR    szInsert;
   char        szAnsiStr[256];

   hList = GetDlgItem(hDlg, ID_PS_DISPLAYLIST);

   i=(UINT)SendMessage(hList, LB_GETCURSEL, 0, 0L);
   if (i != LB_ERR)
   {

      lpItemData = (LPPASTELISTITEMDATA)SendMessage(hList, LB_GETITEMDATA, i, 0L);
      if ((LRESULT)lpItemData == LB_ERR) return;
      nPasteEntriesIndex = lpItemData->nPasteEntriesIndex;
      // Check if there is a '%s' in the lpstrFormatName, then an object is being
      //   pasted/pastelinked. Otherwise Data is being pasted-pastelinked.
#ifdef UNICODE
      W2A((LPOLESTR)lpOPS->arrPasteEntries[nPasteEntriesIndex].lpstrFormatName, szAnsiStr, 256);
#else
      strncpy(szAnsiStr, lpOPS->arrPasteEntries[nPasteEntriesIndex].lpstrFormatName, 256);
#endif

      fIsObject = FHasPercentS(szAnsiStr, lpPS);
   }
   else return;

   // Is DisplayAsIcon checked?
   fDisplayAsIcon=(0L!=(lpPS->dwFlags & PSF_CHECKDISPLAYASICON));

   szInsert = szFullUserTypeName;

   if (lpPS->dwFlags & PSF_SELECTPASTE)     // If user selected Paste
   {
      if (fIsObject)
      {
         iString = (UINT)(fDisplayAsIcon ? IDS_PSPASTEOBJECTASICON : IDS_PSPASTEOBJECT);
         iImage  = (UINT)(fDisplayAsIcon ? RESULTIMAGE_EMBEDICON   : RESULTIMAGE_EMBED);
         szInsert = lpPS->szAppName;
      }
      else
      {
         iString = (UINT)IDS_PSPASTEDATA;
         iImage  = RESULTIMAGE_PASTE;
      }
   }
   else if (lpPS->dwFlags & PSF_SELECTPASTELINK)   // User selected PasteLink
   {
      if (fIsObject)
      {
         iString = (UINT)(fDisplayAsIcon ? IDS_PSPASTELINKOBJECTASICON : IDS_PSPASTELINKOBJECT);
         iImage  = (UINT)(fDisplayAsIcon ? RESULTIMAGE_LINKICON : RESULTIMAGE_LINK);
      }
      else
      {
         iString = (UINT)IDS_PSPASTELINKDATA;
         iImage  = (UINT)RESULTIMAGE_LINK;
      }

   }
   else   // Should never occur.
   {
      iString = (UINT)IDS_PSNONOLE;
      iImage = RESULTIMAGE_PASTE;
   }

   // hBuff contains enough space for the 4 buffers required to build up the help
   //   result text.
   cch = (UINT)GlobalSize(lpPS->hBuff)/4;

   psz1=(LPSTR)GlobalLock(lpPS->hBuff);
   psz2=psz1+cch;
   psz3=psz2+cch;
   psz4=psz3+cch;

   // Default is an empty string.
   *psz1=0;

   if (0!=LoadString(ghInst, iString, psz1, cch))
   {
      char        szAnsiStr2[256];

      // Insert the FullUserTypeName of the source object into the partial result text
      //   specified by the container.
      W2A (szInsert, szAnsiStr, 256);
#ifdef UNICODE
      W2A ((LPOLESTR)lpOPS->arrPasteEntries[nPasteEntriesIndex].lpstrResultText, szAnsiStr2, 256);
#else
      strncpy(szAnsiStr2, lpOPS->arrPasteEntries[nPasteEntriesIndex].lpstrResultText, 256);
#endif

      wsprintf(psz3, (LPSTR)szAnsiStr2, (LPSTR)szAnsiStr);
      // Insert the above partial result text into the standard result text.
      wsprintf(psz4, psz1, (LPSTR)psz3);
      psz1=psz4;
   }

   // If LoadString failed, we simply clear out the results (*psz1=0 above)
   SetDlgItemText(hDlg, ID_PS_RESULTTEXT, psz1);

   // Change the result bitmap
   SendDlgItemMessage(hDlg, ID_PS_RESULTIMAGE, RIM_IMAGESET, iImage, 0L);

   GlobalUnlock(lpPS->hBuff);
}

/*
 * FAddPasteListItem
 *
 * Purpose:
 *  Adds an item to the list box
 *
 * Parameters:
 *  hList            HWND List into which item is to be added
 *  fInsertFirst     BOOL Insert in the beginning of the list?
 *  nPasteEntriesIndex int Index of Paste Entry array this list item corresponsds to
 *  lpPS             Paste Special Dialog Structure
 *  pIMalloc         LPMALLOC  Memory Allocator
 *  lpszBuf          LPSTR Scratch buffer to build up string for list entry
 *  lpszFullUserTypeName LPSTR full user type name for object entry
 *
 * Return Value:
 *  BOOL            TRUE if sucessful.
 *                  FALSE if unsucessful.
 */
BOOL FAddPasteListItem(
      HWND hList, BOOL fInsertFirst, int nPasteEntriesIndex,
      LPPASTESPECIAL lpPS,
      LPMALLOC pIMalloc, LPSTR lpszBuf, LPSTR lpszFullUserTypeName)
{
   LPOLEUIPASTESPECIAL lpOPS = lpPS->lpOPS;
   LPPASTELISTITEMDATA lpItemData;
   int                 nIndex;
   char                szAnsiStr[256];

   // Allocate memory for each list box item
   lpItemData = (LPPASTELISTITEMDATA)pIMalloc->lpVtbl->Alloc(
         pIMalloc, (DWORD)sizeof(PASTELISTITEMDATA));
   if (NULL == lpItemData)
      return FALSE;

   // Fill data associated with each list box item
   lpItemData->nPasteEntriesIndex = nPasteEntriesIndex;
   lpItemData->fCntrEnableIcon = ((lpOPS->arrPasteEntries[nPasteEntriesIndex].dwFlags &
         OLEUIPASTE_ENABLEICON) ? TRUE : FALSE);

   // Build list box entry string, insert the string and add the data the corresponds to it
#ifdef UNICODE
   W2A((LPOLESTR)lpOPS->arrPasteEntries[nPasteEntriesIndex].lpstrFormatName, szAnsiStr, 256);
#else
   strncpy(szAnsiStr,lpOPS->arrPasteEntries[nPasteEntriesIndex].lpstrFormatName, 256);
#endif
   wsprintf((LPSTR)lpszBuf, (LPSTR)szAnsiStr, (LPSTR)lpszFullUserTypeName);

   // only add to listbox if not a duplicate
   if (LB_ERR!=SendMessage(hList,LB_FINDSTRING, 0, (LPARAM)(LPSTR)lpszBuf)) {
      // item is already in list; SKIP this one
      pIMalloc->lpVtbl->Free(pIMalloc, (LPVOID)lpItemData);
      return TRUE;    // this is NOT an error
   }

   nIndex = (int)SendMessage(
         hList,
         (fInsertFirst ? LB_INSERTSTRING : LB_ADDSTRING),
         0,
         (LPARAM)(LPSTR)lpszBuf
   );
   SendMessage(
         hList,
         LB_SETITEMDATA,
         nIndex,
         (LPARAM)(LPPASTELISTITEMDATA)lpItemData
   );
   return TRUE;
}


/*
 * FFillPasteList
 *
 * Purpose:
 *  Fills the invisible paste list with the formats offered by the clipboard object and
 *  asked for by the container.
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  lpPS             Paste Special Dialog Structure
 *
 * Return Value:
 *  BOOL            TRUE if sucessful and if formats could be found.
 *                  FALSE if unsucessful or if no formats could be found.
 */
BOOL FFillPasteList(HWND hDlg, LPPASTESPECIAL lpPS)
{
   LPOLEUIPASTESPECIAL lpOPS = lpPS->lpOPS;
   LPMALLOC            pIMalloc     = NULL;
   LPSTR               lpszBuf      = (LPSTR)GlobalLock(lpPS->hBuff);
   HWND                hList;
   int                 i, j;
   int                 nItems = 0;
   int                 nDefFormat = -1;
   BOOL                fTryObjFmt = FALSE;
   BOOL                fInsertFirst;
   BOOL                fExclude;
   HRESULT             hrErr;
   char                szAnsiStr[256];

   hrErr = CoGetMalloc(MEMCTX_TASK, &pIMalloc);
   if (hrErr != NOERROR)
      goto error;

   hList = GetDlgItem(hDlg, ID_PS_PASTELIST);

   // Loop over the target's priority list of formats
   for (i = 0; i < lpOPS->cPasteEntries; i++)
   {
      if (lpOPS->arrPasteEntries[i].dwFlags != OLEUIPASTE_PASTEONLY &&
            !(lpOPS->arrPasteEntries[i].dwFlags & OLEUIPASTE_PASTE))
         continue;

      fInsertFirst = FALSE;

      if (lpOPS->arrPasteEntries[i].fmtetc.cfFormat==cfFileName
            || lpOPS->arrPasteEntries[i].fmtetc.cfFormat==cfEmbeddedObject
            || lpOPS->arrPasteEntries[i].fmtetc.cfFormat==cfEmbedSource) {
         if (! fTryObjFmt) {
            fTryObjFmt = TRUE;      // only use 1st object format
            fInsertFirst = TRUE;    // OLE obj format should always be 1st

            //Check if this CLSID is in the exclusion list.
            fExclude=FALSE;

            for (j=0; j < (int)lpOPS->cClsidExclude; j++)
            {
               if (IsEqualCLSID(&lpPS->clsidOD,
                            (LPCLSID)(lpOPS->lpClsidExclude+j)))
               {
                  fExclude=TRUE;
                  break;
               }
            }

            if (fExclude)
               continue;   // don't add the object entry to list

         } else {
            continue;   // already added an object format to list
         }
      }

      // add to list if entry is marked TRUE
      if (lpOPS->arrPasteEntries[i].dwScratchSpace) {
         if (nDefFormat < 0)
            nDefFormat = (fInsertFirst ? 0 : nItems);
         else if (fInsertFirst)
            nDefFormat++;   // adjust for obj fmt inserted 1st in list

         W2A (lpPS->szFullUserTypeNameOD, szAnsiStr, 256);
         if (!FAddPasteListItem(hList, fInsertFirst, i, lpPS, pIMalloc,
                  lpszBuf, szAnsiStr))
            goto error;
         nItems++;
      }
   }

   // initialize selection to first format matched in list
   if (nDefFormat >= 0)
      lpPS->nPasteListCurSel = nDefFormat;

   // Clean up
   if (pIMalloc)
      pIMalloc->lpVtbl->Release(pIMalloc);
   if (lpszBuf)
      GlobalUnlock(lpPS->hBuff);

   // If no items have been added to the list box (none of the formats
   //   offered by the source matched those acceptable to the container),
   //   return FALSE
   if (nItems > 0)
      return TRUE;
   else
      return FALSE;

error:
   if (pIMalloc)
      pIMalloc->lpVtbl->Release(pIMalloc);
   if (lpszBuf)
      GlobalUnlock(lpPS->hBuff);
   FreeListData(hList);

   return FALSE;
}


/*
 * FFillPasteLinkList
 *
 * Purpose:
 *  Fills the invisible paste link list with the formats offered by the clipboard object and
 *  asked for by the container.
 *
 * Parameters:
 *  hDlg            HWND of the dialog
 *  lpPS             Paste Special Dialog Structure
 *
 * Return Value:
 *  BOOL            TRUE if sucessful and if formats could be found.
 *                  FALSE if unsucessful or if no formats could be found.
 */
BOOL FFillPasteLinkList(HWND hDlg, LPPASTESPECIAL lpPS)
{
   LPOLEUIPASTESPECIAL lpOPS        = lpPS->lpOPS;
   LPDATAOBJECT        lpSrcDataObj = lpOPS->lpSrcDataObj;
   LPENUMFORMATETC     lpEnumFmtEtc = NULL;
   LPMALLOC            pIMalloc     = NULL;
   LPSTR               lpszBuf      = (LPSTR)GlobalLock(lpPS->hBuff);
   OLEUIPASTEFLAG      pasteFlag;
   UINT arrLinkTypesSupported[PS_MAXLINKTYPES];  // Array of flags that
                                      // indicate which link types
                                      // are supported by source.
   FORMATETC           fmtetc;
   int                 i, j;
   int                 nItems = 0;
   BOOL                fLinkTypeSupported = FALSE;
   HWND                hList;
   int                 nDefFormat = -1;
   BOOL                fTryObjFmt = FALSE;
   BOOL                fInsertFirst;
   HRESULT             hrErr;
   char                szAnsiStr[256];

   hrErr = CoGetMalloc(MEMCTX_TASK, &pIMalloc);
   if (hrErr != NOERROR)
      goto error;

   // Remember which link type formats are offered by lpSrcDataObj.
   _fmemset(&fmtetc, 0, sizeof(FORMATETC));
   for (i = 0; i < lpOPS->cLinkTypes; i++)
   {
      if (lpOPS->arrLinkTypes[i] = cfLinkSource) {
         OLEDBG_BEGIN2("OleQueryLinkFromData called\r\n")
         hrErr = OleQueryLinkFromData(lpSrcDataObj);
         OLEDBG_END2
         if(NOERROR == hrErr)
         {
            arrLinkTypesSupported[i] = 1;
            fLinkTypeSupported = TRUE;
         }
         else arrLinkTypesSupported[i] = 0;
      }
      else {
         fmtetc.cfFormat = lpOPS->arrLinkTypes[i];
         fmtetc.dwAspect = DVASPECT_CONTENT;
         fmtetc.tymed    = 0xFFFFFFFF;       // All tymed values
         fmtetc.lindex   = -1;
         OLEDBG_BEGIN2("IDataObject::QueryGetData called\r\n")
         hrErr = lpSrcDataObj->lpVtbl->QueryGetData(lpSrcDataObj,&fmtetc);
         OLEDBG_END2
         if(NOERROR == hrErr)
         {
            arrLinkTypesSupported[i] = 1;
            fLinkTypeSupported = TRUE;
         }
         else arrLinkTypesSupported[i] = 0;
      }
   }
   // No link types are offered by lpSrcDataObj
   if (! fLinkTypeSupported) {
      nItems = 0;
      goto cleanup;
   }

   hList = GetDlgItem(hDlg, ID_PS_PASTELINKLIST);

   // Enumerate the formats acceptable to container
   for (i = 0; i < lpOPS->cPasteEntries; i++)
   {
      fLinkTypeSupported = FALSE;

      // If container will accept any link type offered by source object
      if (lpOPS->arrPasteEntries[i].dwFlags & OLEUIPASTE_LINKANYTYPE)
         fLinkTypeSupported = TRUE;
      else
      {
         // Check if any of the link types offered by the source
         //    object are acceptable to the container
         // This code depends on the LINKTYPE enum values being powers of 2
         for (pasteFlag = OLEUIPASTE_LINKTYPE1, j = 0;
             j < lpOPS->cLinkTypes;
             pasteFlag*=2, j++)
         {
            if ((lpOPS->arrPasteEntries[i].dwFlags & pasteFlag) &&
                  arrLinkTypesSupported[j])
            {
               fLinkTypeSupported = TRUE;
               break;
            }
         }
      }

      fInsertFirst = FALSE;

      if (lpOPS->arrPasteEntries[i].fmtetc.cfFormat==cfFileName
            || lpOPS->arrPasteEntries[i].fmtetc.cfFormat==cfLinkSource) {
         if (! fTryObjFmt) {
            fTryObjFmt = TRUE;      // only use 1st object format
            fInsertFirst = TRUE;    // OLE obj format should always be 1st
         } else {
            continue;   // already added an object format to list
         }
      }

      // add to list if entry is marked TRUE
      if (fLinkTypeSupported && lpOPS->arrPasteEntries[i].dwScratchSpace) {
         if (nDefFormat < 0)
            nDefFormat = (fInsertFirst ? 0 : nItems);
         else if (fInsertFirst)
            nDefFormat++;   // adjust for obj fmt inserted 1st in list

          W2A (lpPS->szFullUserTypeNameLSD, szAnsiStr, 256);
         if (!FAddPasteListItem(hList, fInsertFirst, i, lpPS, pIMalloc,
                  lpszBuf, szAnsiStr))
            goto error;
         nItems++;
      }
   } // end FOR

   nItems = (int)SendMessage(hList, LB_GETCOUNT, 0, 0L);

   // initialize selection to first format matched in list
   if (nDefFormat >= 0)
      lpPS->nPasteLinkListCurSel = nDefFormat;

cleanup:
   // Clean up
   if (pIMalloc)
      pIMalloc->lpVtbl->Release(pIMalloc);
   if (lpszBuf)
      GlobalUnlock(lpPS->hBuff);

   // If no items have been added to the list box (none of the formats
   //   offered by the source matched those acceptable to the destination),
   //   return FALSE
   if (nItems > 0)
      return TRUE;
   else
      return FALSE;

error:
   if (pIMalloc)
      pIMalloc->lpVtbl->Release(pIMalloc);
   if (lpszBuf)
      GlobalUnlock(lpPS->hBuff);
   FreeListData(hList);

   return FALSE;
}


/*
 * FreeListData
 *
 * Purpose:
 *  Free the local memory associated with each list box item
 *
 * Parameters:
 *  hList           HWND of the list
 *
 * Return Value:
 *  None
 */
void FreeListData(HWND hList)
{
   int                nItems, i;
   LPPASTELISTITEMDATA lpItemData;
   LPMALLOC           pIMalloc;
   HRESULT            hrErr;

   hrErr = CoGetMalloc(MEMCTX_TASK, &pIMalloc);
   if (hrErr != NOERROR)
      return;

   nItems = (int) SendMessage(hList, LB_GETCOUNT, 0, 0L);
   for (i = 0; i < nItems; i++)
   {
      lpItemData = (LPPASTELISTITEMDATA)SendMessage(hList, LB_GETITEMDATA, (WPARAM)i, 0L);
      if ((LRESULT)lpItemData != LB_ERR)
         pIMalloc->lpVtbl->Free(pIMalloc, (LPVOID)lpItemData);
   }
   pIMalloc->lpVtbl->Release(pIMalloc);
}

/*
 * FHasPercentS
 *
 * Purpose:
 *  Determines if string contains %s.
 *
 * Parameters:
 *  lpsz            LPCSTR string in which occurence of '%s' is looked for
 *
 * Return Value:
 *  BOOL            TRUE if %s is found, else FALSE.
 */

BOOL FHasPercentS(LPCSTR lpsz, LPPASTESPECIAL lpPS)
{
   int n = 0;
   LPSTR lpszTmp;

   if (!lpsz) return FALSE;
   // Copy input string to buffer. This allows caller to pass a
   //   code-based string. Code segments may be swapped out in low memory situations
   //   and so code-based strings need to be copied before string elements can be accessed.
   lpszTmp = (LPSTR)GlobalLock(lpPS->hBuff);
   lstrcpy(lpszTmp, lpsz);

   while (*lpszTmp)
   {
      if (*lpszTmp == '%')
      {
         lpszTmp = AnsiNext(lpszTmp);
         if (*lpszTmp == 's')            // If %s, return
         {
            GlobalUnlock(lpPS->hBuff);
            return TRUE;
         }
         else if (*lpszTmp == '%')        // if %%, skip to next character
            lpszTmp = AnsiNext(lpszTmp);
      }
      else lpszTmp = AnsiNext(lpszTmp);
   }

   GlobalUnlock(lpPS->hBuff);
   return FALSE;
}

/*
 * AllocateScratchMem
 *
 * Purpose:
 *  Allocates scratch memory for use by the PasteSpecial dialog. The memory is
 *  is used as the buffer for building up strings using wsprintf. Strings are built up
 *  using the buffer while inserting items into the Paste & PasteLink lists and while
 *  setting the help result text. It must be big  enough to handle the string that results after
 *  replacing the %s in the lpstrFormatName and lpstrResultText in arrPasteEntries[]
 *  by the FullUserTypeName. It must also be big enough to build the dialog's result text
 *  after %s substitutions by the FullUserTypeName or the ApplicationName.
 *
 * Parameters:
 *  lpPS             Paste Special Dialog Structure
 *
 * Return Value:
 *  HGLOBAL         Handle to allocated global memory
 */

HGLOBAL AllocateScratchMem(LPPASTESPECIAL lpPS)
{
   LPOLEUIPASTESPECIAL lpOPS = lpPS->lpOPS;
   int nLen, i;
   int nSubstitutedText = 0;
   int nAlloc = 0;

   // Get the maximum length of the FullUserTypeNames specified by OBJECTDESCRIPTOR
   //   and the LINKSRCDESCRIPTOR and the Application Name. Any of these may be substituted
   //   for %s in the result-text/list entries.
   if (lpPS->szFullUserTypeNameOD)
      nSubstitutedText = OLESTRLEN(lpPS->szFullUserTypeNameOD);
   if (lpPS->szFullUserTypeNameLSD)
      nSubstitutedText = __max(nSubstitutedText, OLESTRLEN(lpPS->szFullUserTypeNameLSD));
   if (lpPS->szAppName)
      nSubstitutedText = __max(nSubstitutedText, OLESTRLEN(lpPS->szAppName));

   // Get the maximum length of lpstrFormatNames & lpstrResultText in arrPasteEntries
   nLen = 0;
   for (i = 0; i < lpOPS->cPasteEntries; i++)
   {
      nLen = __max(nLen, lstrlen(lpOPS->arrPasteEntries[i].lpstrFormatName));
      nLen = __max(nLen, lstrlen(lpOPS->arrPasteEntries[i].lpstrResultText));
   }

   // Get the maximum length of lpstrFormatNames and lpstrResultText after %s  has
   //   been substituted (At most one %s can appear in each string).
   //   Add 1 to hold NULL terminator.
   nAlloc = nLen+nSubstitutedText+1;

   // Allocate scratch memory to be used to build strings
   // nAlloc is big enough to hold any of the lpstrResultText or lpstrFormatName in arrPasteEntries[]
   //   after %s substitution.
   // We also need space to build up the help result text. 512 is the maximum length of the
   //   standard dialog help text before substitutions. 512+nAlloc is the maximum length
   //   after %s substition.
   // SetPasteSpecialHelpResults() requires 4 such buffers to build up the result text
   return GlobalAlloc(GHND, (DWORD)4*(512+nAlloc));
}
