/*
 * ICON.C
 *
 * Implements the OleUIChangeIcon function which invokes the complete
 * Change Icon dialog.
 *
 * Copyright (c)1992 Microsoft Corporation, All Right Reserved
 */

#define STRICT  1
#include "ole2ui.h"
#include "common.h"
#include "utility.h"
#include "icon.h"
#include "geticon.h"

/*
 * OleUIChangeIcon
 *
 * Purpose:
 *  Invokes the standard OLE Change Icon dialog box allowing the user
 *  to select an icon from an icon file, executable, or DLL.
 *
 * Parameters:
 *  lpCI            LPOLEUIChangeIcon pointing to the in-out structure
 *                  for this dialog.
 *
 * Return Value:
 *  UINT            OLEUI_SUCCESS or OLEUI_OK if all is well, otherwise
 *                  an error value.
 */

STDAPI_(UINT) OleUIChangeIcon(LPOLEUICHANGEICON lpCI)
   {
   UINT        uRet;
   HGLOBAL     hMemDlg=NULL;

   uRet=UStandardValidation((LPOLEUISTANDARD)lpCI, sizeof(OLEUICHANGEICON)
                      , &hMemDlg);

   if (OLEUI_SUCCESS!=uRet)
      return uRet;

#if defined( OBSOLETE )
   if (NULL==lpCI->hMetaPict)
      uRet=OLEUI_CIERR_MUSTHAVECURRENTMETAFILE;
#endif

   if (lpCI->dwFlags & CIF_USEICONEXE)
   {
     if (   (NULL == lpCI->szIconExe)
        || (IsBadReadPtr(lpCI->szIconExe, lpCI->cchIconExe))
        || (IsBadWritePtr(lpCI->szIconExe, lpCI->cchIconExe)) )
       uRet = OLEUI_CIERR_SZICONEXEINVALID;

   }

 // REVIEW: how do we validate the CLSID?
/*
   if ('\0'==*((LPOLESTR)&lpCI->clsid))
      uRet=OLEUI_CIERR_MUSTHAVECLSID;
*/
   if (OLEUI_ERR_STANDARDMIN <= uRet)
      {
      if (NULL!=hMemDlg)
         FreeResource(hMemDlg);

      return uRet;
      }

   //Now that we've validated everything, we can invoke the dialog.
   return UStandardInvocation(ChangeIconDialogProc, (LPOLEUISTANDARD)lpCI
                        , hMemDlg, MAKEINTRESOURCE(IDD_CHANGEICON));
   }





/*
 * ChangeIconDialogProc
 *
 * Purpose:
 *  Implements the OLE Change Icon dialog as invoked through the
 *  OleUIChangeIcon function.
 *
 * Parameters:
 *  Standard
 *
 * Return Value:
 *  Standard
 */

BOOL CALLBACK EXPORT ChangeIconDialogProc(HWND hDlg, UINT iMsg
                            , WPARAM wParam, LPARAM lParam)
   {
   LPCHANGEICON            lpCI;
   HICON                   hIcon;
   HGLOBAL                 hMetaPict;
   BOOL                    fOK=FALSE;
   UINT                    uRet=0;
   LPOLESTR                psz;
   char                    szTemp[OLEUI_CCHPATHMAX], tmp[OLEUI_CCHPATHMAX];
   OLECHAR                 szUniTemp[OLEUI_CCHPATHMAX];

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

   lpCI=(LPCHANGEICON)LpvStandardEntry(hDlg, iMsg, wParam, lParam, &uRet);

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

   //Process the temination message
   if (iMsg==uMsgEndDialog)
      {
      //Insure that icons are properly destroyed.
      SendDlgItemMessage(hDlg, ID_ICONLIST, LB_RESETCONTENT, 0, 0L);

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

   switch (iMsg)
      {
      case WM_INITDIALOG:
         FChangeIconInit(hDlg, wParam, lParam);
         return TRUE;


      case WM_MEASUREITEM:
         {
         LPMEASUREITEMSTRUCT     lpMI=(LPMEASUREITEMSTRUCT)lParam;

         //All icons are system metric+padding in width and height
         lpMI->itemWidth =GetSystemMetrics(SM_CXICON)+CXICONPAD;
         lpMI->itemHeight=GetSystemMetrics(SM_CYICON)+CYICONPAD;
         }
         break;


      case WM_DRAWITEM:
         return FDrawListIcon((LPDRAWITEMSTRUCT)lParam);


      case WM_DELETEITEM:
         //Free the GDI object for the item
         DestroyIcon((HICON)LOWORD(((LPDELETEITEMSTRUCT)lParam)->itemData));
         break;


      case WM_COMMAND:
         switch (wID)
            {
            case ID_CURRENT:
            case ID_DEFAULT:
            case ID_FROMFILE:
               UpdateResultIcon(lpCI, hDlg, wID);
               break;

            case ID_LABELEDIT:
               //When the edit loses focus, update the result display
               if (EN_KILLFOCUS==wCode)
                  {
                  GetDlgItemText(hDlg, ID_LABELEDIT, szTemp, OLEUI_CCHPATHMAX);
                  SetDlgItemText(hDlg, ID_RESULTLABEL, szTemp);
                  }
               break;

            case ID_FROMFILEEDIT:
               //If the text changed, remove any selection in the list.
               GetDlgItemText(hDlg, ID_FROMFILEEDIT, szTemp, OLEUI_CCHPATHMAX);

               W2A (lpCI->szFile, tmp, OLEUI_CCHPATHMAX);
               if (lpCI && lstrcmpi(szTemp, tmp))
                  {
                  SendDlgItemMessage(hDlg, ID_ICONLIST, LB_SETCURSEL
                                 , (WPARAM)-1, 0);

                  //Also force selection of ID_FROMFILE
                  CheckRadioButton(hDlg, ID_CURRENT, ID_FROMFILE, ID_FROMFILE);
                  }
               break;


            case ID_ICONLIST:
               switch (wCode)
                  {
                  case LBN_SETFOCUS:
                     //If we got the focus, see about updating.
                     GetDlgItemText(hDlg, ID_FROMFILEEDIT, szTemp
                                 , OLEUI_CCHPATHMAX);

                     //Check if file changed and update the list if so
                     W2A (lpCI->szFile, tmp, OLEUI_CCHPATHMAX);
                     if (lpCI && 0!=lstrcmpi(szTemp, tmp))
                        {
                        A2W (szTemp, lpCI->szFile, OLEUI_CCHPATHMAX);
                        UFillIconList(hDlg, ID_ICONLIST, szTemp);
                        UpdateResultIcon(lpCI, hDlg, ID_FROMFILE);
                        }
                     break;

                  case LBN_SELCHANGE:
                     UpdateResultIcon(lpCI, hDlg, ID_FROMFILE);
                     break;

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


            case ID_BROWSE:
            {
               DWORD dwOfnFlags;

               /*
                * To allow the hook to customize the browse dialog, we
                * send OLEUI_MSG_BROWSE.  If the hook returns FALSE
                * we use the default, otherwise we trust that it retrieved
                * a filename for us.  This mechanism prevents hooks from
                * trapping ID_BROWSE to customize the dialog and from
                * trying to figure out what we do after we have the name.
                */

               //Copy for reference
               W2A (lpCI->szFile, szTemp, OLEUI_CCHPATHMAX);
               uRet=UStandardHook(lpCI, hDlg, uMsgBrowse, OLEUI_CCHPATHMAX
                              , (LONG)szTemp);

               dwOfnFlags = OFN_FILEMUSTEXIST;
               if (lpCI->lpOCI->dwFlags & CIF_SHOWHELP)
                  dwOfnFlags |= OFN_SHOWHELP;

               if (0==uRet)
                  uRet=(BOOL)Browse(hDlg, szTemp, NULL, OLEUI_CCHPATHMAX, (UINT)IDS_ICONFILTERS, dwOfnFlags);

               /*
                * Only reinitialize if the file changed, so if we got
                * TRUE from the hook but the user hit Cancel, we don't
                * spend time unecessarily refilling the list.
                */
               W2A (lpCI->szFile, tmp, OLEUI_CCHPATHMAX);
               if (0!=uRet && 0!=lstrcmpi(szTemp, tmp))
               {
                  CheckRadioButton(hDlg, ID_CURRENT, ID_FROMFILE, ID_FROMFILE);
                  SetDlgItemText(hDlg, ID_FROMFILEEDIT, tmp);
                  UFillIconList(hDlg, ID_ICONLIST, tmp);
                  UpdateResultIcon(lpCI, hDlg, ID_FROMFILE);
               }
            }
            break;


            case IDOK:
               /*
                * If the user pressed enter, compare the current file
                * and the one we have stored.  If they match, then
                * refill the listbox instead of closing down.  This is
                * so the user can press Enter in the edit control as
                * they would expect to be able to do.
                */
               GetDlgItemText(hDlg, ID_FROMFILEEDIT, szTemp, OLEUI_CCHPATHMAX);

               //Check if the file changed at all.
               W2A (lpCI->szFile, tmp, OLEUI_CCHPATHMAX);
               if (0!=lstrcmpi(szTemp, tmp))
                  {
                  A2W (szTemp, lpCI->szFile, OLEUI_CCHPATHMAX);
                  UFillIconList(hDlg, ID_ICONLIST, szTemp);
                  UpdateResultIcon(lpCI, hDlg, ID_FROMFILE);

                  //Eat this message to prevent focus change.
                  return TRUE;
                  }

               // Check if the file name is valid
               //  (if FromFile is enabled)
               if (ID_FROMFILE & lpCI->dwFlags)
                  {
                  OFSTRUCT    of;
                  HWND hWnd;

                  W2A (lpCI->szFile, tmp, OLEUI_CCHPATHMAX);
                  if (HFILE_ERROR==DoesFileExist(tmp, &of))
                     {
                     OpenFileError(hDlg, of.nErrCode, tmp);
                     hWnd = GetDlgItem(hDlg, ID_FROMFILEEDIT);
                     SetFocus(hWnd);
                     SendMessage(hWnd, EM_SETSEL, 0, MAKELPARAM(0, (WORD)-1));
                     return TRUE;  // eat this message
                     }
                  }

               if ((HWND)LOWORD(lParam) != GetFocus())
                  SetFocus((HWND)LOWORD(lParam));

               /*
                * On closing, create a new metafilepict with the
                * current icon and label, destroying the old structure.
                *
                * Since we make a copy of the icon by placing it into
                * the metafile, we have to make sure we delete the
                * icon in the current field.  When the listbox is
                * destroyed WM_DELETEITEMs will clean it up appropriately.
                */

               hIcon=(HICON)SendDlgItemMessage(hDlg, ID_RESULTICON
                                       , STM_GETICON, 0, 0L);

               /*
                * If default is selected then we get the source
                * information from registrion database for the
                * current class to put in the metafile.  If current
                * is selected the we just retrieve the original file
                * again and recreate the metafile.  If from file is
                * selected we use the current filename from the
                * control and the current listbox selection.
                */

               psz=lpCI->szFile;

               if (lpCI->dwFlags & CIF_SELECTDEFAULT)
                  {
                  psz=lpCI->szDefIconFile;
                  lpCI->iIcon=lpCI->iDefIcon;
                  hIcon=lpCI->hDefIcon;
                  }

               if (lpCI->dwFlags & CIF_SELECTCURRENT)
                  {
                  //Go get the current icon source back.
                  OleUIMetafilePictExtractIconSource(lpCI->lpOCI->hMetaPict
                     , psz, &lpCI->iIcon);
                  }

               if (lpCI->dwFlags & CIF_SELECTFROMFILE)
                  {
                  GetDlgItemText(hDlg, ID_FROMFILEEDIT, tmp, OLEUI_CCHPATHMAX);
                  A2W (tmp, psz, OLEUI_CCHPATHMAX);

                  lpCI->iIcon=(UINT)SendDlgItemMessage(hDlg
                     , ID_ICONLIST, LB_GETCURSEL, 0, 0L);
                  }


               //Get the label and go create the metafile
               GetDlgItemText(hDlg, ID_LABELEDIT, szTemp, OLEUI_CCHPATHMAX);

               //If psz is NULL (default) we get no source comments.
               A2W (szTemp, szUniTemp, OLEUI_CCHPATHMAX);
               hMetaPict=OleUIMetafilePictFromIconAndLabel(hIcon
                  , szUniTemp, psz, lpCI->iIcon);

               //Clean up the current icon that we extracted.
               hIcon=(HICON)SendDlgItemMessage(hDlg, ID_CURRENTICON
                                       , STM_GETICON, 0, 0L);
               DestroyIcon(hIcon);

               //Clean up the default icon
               DestroyIcon(lpCI->hDefIcon);

               // Remove the prop set on our parent
               RemoveProp(lpCI->lpOCI->hWndOwner, PROP_HWND_CHGICONDLG);

               if (NULL==hMetaPict)
                  SendMessage(hDlg, uMsgEndDialog, OLEUI_FALSE, 0L);

               OleUIMetafilePictIconFree(lpCI->lpOCI->hMetaPict);
               lpCI->lpOCI->hMetaPict=hMetaPict;

               lpCI->lpOCI->dwFlags = lpCI->dwFlags;

               SendMessage(hDlg, uMsgEndDialog, OLEUI_OK, 0L);
               break;


            case IDCANCEL:
               //Clean up the current icon that we extracted.
               hIcon=(HICON)SendDlgItemMessage(hDlg, ID_CURRENTICON
                                       , STM_GETICON, 0, 0L);
               DestroyIcon(hIcon);

               //Clean up the default icon
               DestroyIcon(lpCI->hDefIcon);

               // Remove the prop set on our parent
               RemoveProp(lpCI->lpOCI->hWndOwner, PROP_HWND_CHGICONDLG);

               //We leave hMetaPict intact on Cancel; caller's responsibility
               SendMessage(hDlg, uMsgEndDialog, OLEUI_CANCEL, 0L);
               break;


            case ID_OLEUIHELP:
               PostMessage(lpCI->lpOCI->hWndOwner, uMsgHelp,
                        (WPARAM)hDlg, MAKELPARAM(IDD_CHANGEICON, 0));
               break;
            }
         break;

      default:
      {
         if (lpCI && iMsg == lpCI->nBrowseHelpID) {
            PostMessage(lpCI->lpOCI->hWndOwner, uMsgHelp,
                  (WPARAM)hDlg, MAKELPARAM(IDD_CHANGEICONBROWSE, 0));
         }
      }
      break;
      }
   return FALSE;
   }




/*
 * FChangeIconInit
 *
 * Purpose:
 *  WM_INITIDIALOG handler for the Change Icon 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 FChangeIconInit(HWND hDlg, WPARAM wParam, LPARAM lParam)
   {
   LPCHANGEICON            lpCI;
   LPOLEUICHANGEICON       lpOCI;
   HFONT                   hFont;
   HWND                    hList;
   UINT                    cyList;
   RECT                    rc, rcG;
   UINT                    uID;
   char                    szAnsiStr[256];

   //1.  Copy the structure at lParam into our instance memory.
   lpCI=(LPCHANGEICON)LpvStandardInit(hDlg, sizeof(CHANGEICON), TRUE, &hFont);

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

   //Save the original pointer and copy necessary information.
   lpOCI=(LPOLEUICHANGEICON)lParam;

   lpCI->lpOCI  =lpOCI;
   lpCI->dwFlags=lpOCI->dwFlags;

   //Go extract the icon source from the metafile.
   OleUIMetafilePictExtractIconSource(lpOCI->hMetaPict, lpCI->szFile, &lpCI->iIcon);

   //Go extract the icon and the label from the metafile
   OleUIMetafilePictExtractLabel(lpOCI->hMetaPict, lpCI->szLabel, OLEUI_CCHLABELMAX, NULL);
   lpCI->hCurIcon=OleUIMetafilePictExtractIcon(lpOCI->hMetaPict);

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


   //3.  Show or hide the help button
   if (!(lpCI->dwFlags & CIF_SHOWHELP))
      StandardShowDlgItem(hDlg, ID_OLEUIHELP, SW_HIDE);


   /*
    * 4.  Set text limits and initial control values.  If we're given
    *     an intial label we set it in the edit and static controls.
    *     If we don't, then we copy the default contents of the static
    *     control into the edit control, meaning that only the default
    *     static control string need be localized.
    */

   SendDlgItemMessage(hDlg, ID_LABELEDIT, EM_LIMITTEXT, OLEUI_CCHLABELMAX, 0L);
   SendDlgItemMessage(hDlg, ID_FROMFILEEDIT, EM_LIMITTEXT, OLEUI_CCHPATHMAX,  0L);
   W2A (lpCI->szFile, szAnsiStr, 256);
   SetDlgItemText(hDlg, ID_FROMFILEEDIT, szAnsiStr);

   //Copy the label text into the edit and static controls.
   W2A (lpCI->szLabel, szAnsiStr, 256);
   SetDlgItemText(hDlg, ID_LABELEDIT,   szAnsiStr);
   SetDlgItemText(hDlg, ID_RESULTLABEL, szAnsiStr);


   lpCI->hDefIcon = NULL;

   if (lpCI->dwFlags & CIF_USEICONEXE)
   {
      W2A (lpCI->lpOCI->szIconExe, szAnsiStr, 256);
      lpCI->hDefIcon = ExtractIcon(ghInst, szAnsiStr, 0);

      if (NULL != lpCI->hDefIcon)
      {
       OLESTRCPY (lpCI->szDefIconFile, lpCI->lpOCI->szIconExe);
       lpCI->iDefIcon = 0;
      }
   }


   if (NULL == lpCI->hDefIcon)
   {
      HGLOBAL hMetaPict;

      hMetaPict = GetIconOfClass(ghInst,
                          &lpCI->lpOCI->clsid,
                          NULL,
                          TRUE);

      lpCI->hDefIcon = OleUIMetafilePictExtractIcon(hMetaPict);

      OleUIMetafilePictExtractIconSource(hMetaPict,
                                lpCI->szDefIconFile,
                                &lpCI->iDefIcon);

      OleUIMetafilePictIconFree(hMetaPict);
   }


   //Initialize all the icon displays.
   SendDlgItemMessage(hDlg, ID_CURRENTICON, STM_SETICON
      , (WPARAM)lpCI->hCurIcon, 0L);
   SendDlgItemMessage(hDlg, ID_DEFAULTICON, STM_SETICON
      , (WPARAM)lpCI->hDefIcon, 0L);
   SendDlgItemMessage(hDlg, ID_RESULTICON,  STM_SETICON
      , (WPARAM)lpCI->hCurIcon, 0L);


   /*
    * 5.  Since we cannot predict the size of icons on any display,
    *     we have to resize the icon listbox to the size of an icon
    *     (plus padding), a scrollbar, and two borders (top & bottom).
    */
   cyList=GetSystemMetrics(SM_CYICON)+GetSystemMetrics(SM_CYHSCROLL)
         +GetSystemMetrics(SM_CYBORDER)*2+CYICONPAD;

   hList=GetDlgItem(hDlg, ID_ICONLIST);
   GetClientRect(hList, &rc);
   SetWindowPos(hList, NULL, 0, 0, rc.right, cyList
             , SWP_NOMOVE | SWP_NOZORDER);

   //Set the columns in this multi-column listbox to hold one icon
   SendMessage(hList, LB_SETCOLUMNWIDTH
            , GetSystemMetrics(SM_CXICON)+CXICONPAD,0L);

   /*
    * 5a.  If the listbox expanded below the group box, then size
    *      the groupbox down, move the label static and exit controls
    *      down, and expand the entire dialog appropriately.
    */

   GetWindowRect(hList, &rc);
   GetWindowRect(GetDlgItem(hDlg, ID_GROUP), &rcG);

   if (rc.bottom > rcG.bottom)
      {
      //Calculate amount to move things down.
      cyList=(rcG.bottom-rcG.top)-(rc.bottom-rc.top-cyList);

      //Expand the group box.
      rcG.right -=rcG.left;
      rcG.bottom-=rcG.top;
      SetWindowPos(GetDlgItem(hDlg, ID_GROUP), NULL, 0, 0
                , rcG.right, rcG.bottom+cyList
                , SWP_NOMOVE | SWP_NOZORDER);

      //Expand the dialog box.
      GetClientRect(hDlg, &rc);
      SetWindowPos(hDlg, NULL, 0, 0, rc.right, rc.bottom+cyList
                , SWP_NOMOVE | SWP_NOZORDER);

      //Move the label and edit controls down.
      GetClientRect(GetDlgItem(hDlg, ID_LABEL), &rc);
      SetWindowPos(GetDlgItem(hDlg, ID_LABEL), NULL, 0, cyList
                , rc.right, rc.bottom, SWP_NOSIZE | SWP_NOZORDER);

      GetClientRect(GetDlgItem(hDlg, ID_LABELEDIT), &rc);
      SetWindowPos(GetDlgItem(hDlg, ID_LABELEDIT), NULL, 0, cyList
                , rc.right, rc.bottom, SWP_NOSIZE | SWP_NOZORDER);
      }


   /*
    * 6.  Select Current, Default, or From File radiobuttons appropriately.
    *     The CheckRadioButton call sends WM_COMMANDs which handle
    *     other actions.  Note that if we check From File, which
    *     takes an icon from the list, we better fill the list.
    *     This will also fill the list even if default is selected.
    */

   W2A (lpCI->szFile, szAnsiStr, 256);
   if (0!=UFillIconList(hDlg, ID_ICONLIST, szAnsiStr))
      {
      //If szFile worked, then select the source icon in the listbox.
      SendDlgItemMessage(hDlg, ID_ICONLIST, LB_SETCURSEL, (WPARAM) szAnsiStr, 0L);
      }


   if (lpCI->dwFlags & CIF_SELECTCURRENT)
      CheckRadioButton(hDlg, ID_CURRENT, ID_FROMFILE, ID_CURRENT);
   else
      {
      uID=(lpCI->dwFlags & CIF_SELECTFROMFILE) ? ID_FROMFILE : ID_DEFAULT;
      CheckRadioButton(hDlg, ID_CURRENT, ID_FROMFILE, uID);
      }

   //7.  Give our parent window access to our hDlg (via a special SetProp).
   //    The PasteSpecial dialog may need to force our dialog down if the
   //    clipboard contents change underneath it. if so it will send
   //    us a IDCANCEL command.
   SetProp(lpCI->lpOCI->hWndOwner, PROP_HWND_CHGICONDLG, hDlg);

   lpCI->nBrowseHelpID = RegisterWindowMessage(HELPMSGSTRING);

   //8.  Call the hook with lCustData in lParam
   UStandardHook(lpCI, hDlg, WM_INITDIALOG, wParam, lpOCI->lCustData);
   return TRUE;
   }





/*
 * UFillIconList
 *
 * Purpose:
 *  Given a listbox and a filename, attempts to open that file and
 *  read all the icons that exist therein, adding them to the listbox
 *  hList as owner-draw items.  If the file does not exist or has no
 *  icons, then you get no icons and an appropriate warning message.
 *
 * Parameters:
 *  hDlg            HWND of the dialog containing the listbox.
 *  idList          UINT identifier of the listbox to fill.
 *  pszFile         LPSTR of the file from which to extract icons.
 *
 * Return Value:
 *  UINT            Number of items added to the listbox.  0 on failure.
 */

UINT UFillIconList(HWND hDlg, UINT idList, LPSTR pszFile)
   {
   HWND        hList;
   UINT        i;
   UINT        cIcons=0;
   HCURSOR     hCur;
   HICON       hIcon;
   OFSTRUCT    of;

   if (NULL==hDlg || !IsWindow(hDlg) || NULL==pszFile)
      return 0;

   hList=GetDlgItem(hDlg, idList);

   if (NULL==hList)
      return 0;

   //Clean out the listbox.
   SendMessage(hList, LB_RESETCONTENT, 0, 0L);

   //If we have an empty string, just exit leaving the listbox empty as well
   if (0==lstrlen(pszFile))
      return 0;

   //Turn on the hourglass
   hCur=HourGlassOn();

   //Check if the file is valid.
   if (HFILE_ERROR!=DoesFileExist(pszFile, &of))
      {
      #ifdef EXTRACTICONWORKS
      //Get the icon count for this file.
      cIcons=(UINT)ExtractIcon(ghInst, pszFile, (UINT)-1);
      #else
      /*
       * ExtractIcon in Windows 3.1 with -1 eats a selector, leaving an
       * extra global memory object around for this applciation.  Since
       * changing icons may happen very often with all OLE apps in
       * the system, we have to work around it.  So we'll say we
       * have lots of icons and just call ExtractIcon until it
       * fails.  We check if there's any around by trying to get
       * the first one.
       */
      cIcons=0xFFFF;

      hIcon=ExtractIcon(ghInst, pszFile, 0);

      //Fake a failure with cIcons=0, or cleanup hIcon from this test.
      if (32 > (UINT)hIcon)
         cIcons=0;
      else
         DestroyIcon(hIcon);
      #endif

      if (0!=cIcons)
         {
         SendMessage(hList, WM_SETREDRAW, FALSE, 0L);

         for (i=0; i<cIcons; i++)
            {
            hIcon=ExtractIcon(ghInst, pszFile, i);

            if (32 < (UINT)hIcon)
               SendMessage(hList, LB_ADDSTRING, 0, (LONG)(UINT)hIcon);
            #ifndef EXTRACTICONWORKS
            else
               {
               //ExtractIcon failed, so let's leave now.
               break;
               }
            #endif
            }

         //Force complete repaint
         SendMessage(hList, WM_SETREDRAW, TRUE, 0L);
         InvalidateRect(hList, NULL, TRUE);

         //Select an icon
         SendMessage(hList, LB_SETCURSEL, 0, 0L);
         }
      else
         ErrorWithFile(hDlg, ghInst, (UINT)IDS_CINOICONSINFILE, pszFile, MB_OK);
      }
   else
      OpenFileError(hDlg, of.nErrCode, pszFile);

   HourGlassOff(hCur);
   return cIcons;
   }




/*
 * FDrawListIcon
 *
 * Purpose:
 *  Handles WM_DRAWITEM for the icon listbox.
 *
 * Parameters:
 *  lpDI            LPDRAWITEMSTRUCT from WM_DRAWITEM
 *
 * Return Value:
 *  BOOL            TRUE if we did anything, FALSE if there are no items
 *                  in the list.
 */

BOOL FDrawListIcon(LPDRAWITEMSTRUCT lpDI)
   {
   COLORREF        cr;

   /*
    * If there are no items in the list, then itemID is negative according
    * to the Win3.1 SDK.  Unfortunately DRAWITEMSTRUCT has an unsigned int
    * for this field, so we need the typecast to do a signed comparison.
    */
   if ((int)lpDI->itemID < 0)
      return FALSE;

   /*
    * For selection or draw entire case we just draw the entire item all
    * over again.  For focus cases, we only call DrawFocusRect.
    */

   if (lpDI->itemAction & (ODA_SELECT | ODA_DRAWENTIRE))
      {
      //Clear background and draw the icon.
      if (lpDI->itemState & ODS_SELECTED)
         cr=SetBkColor(lpDI->hDC, GetSysColor(COLOR_HIGHLIGHT));
      else
         cr=SetBkColor(lpDI->hDC, GetSysColor(COLOR_WINDOW));

      //Draw a cheap rectangle.
      ExtTextOut(lpDI->hDC, 0, 0, ETO_OPAQUE, &lpDI->rcItem
               , NULL, 0, NULL);

      DrawIcon(lpDI->hDC, lpDI->rcItem.left+(CXICONPAD/2)
             , lpDI->rcItem.top+(CYICONPAD/2)
             , (HICON)LOWORD(lpDI->itemData));

      //Restore original background for DrawFocusRect
      SetBkColor(lpDI->hDC, cr);
      }

   //Always change focus on the focus action.
   if (lpDI->itemAction & ODA_FOCUS || lpDI->itemState & ODS_FOCUS)
      DrawFocusRect(lpDI->hDC, &lpDI->rcItem);

   return TRUE;
   }





/*
 * UpdateResultIcon
 *
 * Purpose:
 *  Updates the result icon using the current icon in the default display
 *  or the icon listbox depending on fFromDefault.
 *
 * Parameters:
 *  lpCI            LPCHANGEICON containing dialog flags.
 *  hDlg            HWND of the dialog
 *  uID             UINT identifying the radiobutton selected.
 *
 * Return Value:
 *  None
 */

void UpdateResultIcon(LPCHANGEICON lpCI, HWND hDlg, UINT uID)
   {
   UINT        iSel;
   LONG        lTemp=LB_ERR;

   lpCI->dwFlags &= ~(CIF_SELECTCURRENT | CIF_SELECTDEFAULT | CIF_SELECTFROMFILE);

   switch (uID)
      {
      case ID_CURRENT:
         lTemp=SendDlgItemMessage(hDlg, ID_CURRENTICON, STM_GETICON, 0, 0L);
         lpCI->dwFlags |= CIF_SELECTCURRENT;
         break;

      case ID_DEFAULT:
         lTemp=SendDlgItemMessage(hDlg, ID_DEFAULTICON, STM_GETICON, 0, 0L);
         lpCI->dwFlags |= CIF_SELECTDEFAULT;
         break;

      case ID_FROMFILE:
         //Get the selected icon from the list and place it in the result
         lpCI->dwFlags |= CIF_SELECTFROMFILE;

         iSel=(UINT)SendDlgItemMessage(hDlg, ID_ICONLIST, LB_GETCURSEL, 0, 0L);
         if ((UINT)LB_ERR==iSel)
            lTemp=SendDlgItemMessage(hDlg, ID_DEFAULTICON, STM_GETICON, 0, 0L);
         else
            SendDlgItemMessage(hDlg, ID_ICONLIST, LB_GETTEXT, iSel
                        , (LPARAM)(LPLONG)&lTemp);

         break;
      }

   if ((LONG)LB_ERR!=lTemp)
      SendDlgItemMessage(hDlg, ID_RESULTICON, STM_SETICON, LOWORD(lTemp), 0L);
   return;
   }
