/*
 * propsht.cpp - IPropSheetExt implementation for URL class.
 */


/* Headers
 **********/

#include "project.hpp"
#pragma hdrstop

#undef NO_HELP              // for help.h

#include <help.h>

#undef IDH_COMM_GROUPBOX    // for addon.h
#include <addon.h>

#include "resource.h"

#include <mluisupp.h>


/* Types
 ********/

/* Internet Shortcut property sheet data */

typedef struct _isps
{
   PROPSHEETPAGE psp;

   PInternetShortcut pintshcut;

   char rgchIconFile[MAX_PATH_LEN];

   int niIcon;
}
ISPS;
DECLARE_STANDARD_TYPES(ISPS);


/* Module Constants
 *******************/

#pragma data_seg(DATA_SEG_READ_ONLY)

// Tray notification window class

PRIVATE_DATA const char s_cszTrayNotificationClass[]  = WNDCLASS_TRAYNOTIFY;

// HACKHACK: WMTRAY_SCREGISTERHOTKEY and WMTRAY_SCUNREGISTERHOTKEY are stolen
// from shelldll\link.c.

PRIVATE_DATA CUINT WMTRAY_SCREGISTERHOTKEY            = (WM_USER + 233);
PRIVATE_DATA CUINT WMTRAY_SCUNREGISTERHOTKEY             = (WM_USER + 234);

// show commands - N.b., the order of these constants must match the order of
// the corresponding IDS_ string table constants.

PRIVATE_DATA const UINT s_ucMaxShowCmdLen             = MAX_PATH_LEN;

PRIVATE_DATA const int s_rgnShowCmds[] =
{
   SW_SHOWNORMAL,
   SW_SHOWMINNOACTIVE,
   SW_SHOWMAXIMIZED
};

// help files

PRIVATE_DATA const char s_cszPlusHelpFile[]           = "Plus!.hlp";

// help topics

PRIVATE_DATA const DWORD s_rgdwHelpIDs[] =
{
   IDD_LINE_1,          NO_HELP,
   IDD_LINE_2,          NO_HELP,
   IDD_ICON,            IDH_FCAB_LINK_ICON,
   IDD_NAME,            IDH_FCAB_LINK_NAME,
   IDD_URL_TEXT,        IDH_INTERNET_SHORTCUT_TARGET,
   IDD_URL,             IDH_INTERNET_SHORTCUT_TARGET,
   IDD_HOTKEY_TEXT,     IDH_FCAB_LINK_HOTKEY,
   IDD_HOTKEY,          IDH_FCAB_LINK_HOTKEY,
   IDD_START_IN_TEXT,   IDH_FCAB_LINK_WORKING,
   IDD_START_IN,        IDH_FCAB_LINK_WORKING,
   IDD_SHOW_CMD,        IDH_FCAB_LINK_RUN,
   IDD_CHANGE_ICON,     IDH_FCAB_LINK_CHANGEICON,
   0,                   0
};

#pragma data_seg()


/***************************** Private Functions *****************************/


#ifdef DEBUG

PRIVATE_CODE BOOL IsValidPCISPS(PCISPS pcisps)
{
   return(IS_VALID_READ_PTR(pcisps, CISPS) &&
      IS_VALID_STRUCT_PTR(&(pcisps->psp), CPROPSHEETPAGE) &&
      IS_VALID_STRUCT_PTR(pcisps->pintshcut, CInternetShortcut) &&
      EVAL(IsValidIconIndex(*(pcisps->rgchIconFile) ? S_OK : S_FALSE, pcisps->rgchIconFile, sizeof(pcisps->rgchIconFile), pcisps->niIcon)));
}

#endif


#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */

PRIVATE_CODE UINT CALLBACK ISPSCallback(HWND hwnd, UINT uMsg,
                    LPPROPSHEETPAGE ppsp)
{
   UINT uResult = TRUE;
   PISPS pisps = (PISPS)ppsp;

   // uMsg may be any value.

   ASSERT(! hwnd ||
      IS_VALID_HANDLE(hwnd, WND));
   ASSERT(IS_VALID_STRUCT_PTR((PCISPS)ppsp, CISPS));

   switch (uMsg)
   {
      case PSPCB_CREATE:
     TRACE_OUT(("ISPSCallback(): Received PSPCB_CREATE."));
     break;

      case PSPCB_RELEASE:
     TRACE_OUT(("ISPSCallback(): Received PSPCB_RELEASE."));
     pisps->pintshcut->Release();
     break;

      default:
     TRACE_OUT(("ISPSCallback(): Unhandled message %u.",
            uMsg));
     break;
   }

   return(uResult);
}

#pragma warning(default:4100) /* "unreferenced formal parameter" warning */


PRIVATE_CODE void SetISPSIcon(HWND hdlg, HICON hicon)
{
   HICON hiconOld;

   ASSERT(IS_VALID_HANDLE(hdlg, WND));
   ASSERT(IS_VALID_HANDLE(hicon, ICON));

   hiconOld = (HICON)SendDlgItemMessage(hdlg, IDD_ICON, STM_SETICON,
                    (WPARAM)hicon, 0);

   if (hiconOld)
      DestroyIcon(hiconOld);

   TRACE_OUT(("SetISPSIcon(): Set property sheet icon to %#lx.",
          hicon));

   return;
}


PRIVATE_CODE void SetISPSFileNameAndIcon(HWND hdlg)
{
   HRESULT hr;
   PInternetShortcut pintshcut;
   char rgchFile[MAX_PATH_LEN];

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   pintshcut = ((PISPS)GetWindowLongPtr(hdlg, DWLP_USER))->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   hr = pintshcut->GetCurFile(rgchFile, sizeof(rgchFile));

   if (hr == S_OK)
   {
      SHFILEINFO shfi;
      DWORD_PTR dwResult;

      dwResult = SHGetFileInfo(rgchFile, 0, &shfi, sizeof(shfi),
                   (SHGFI_DISPLAYNAME | SHGFI_ICON));

      if (dwResult)
      {
     PSTR pszFileName;

     pszFileName = (PSTR)ExtractFileName(shfi.szDisplayName);

     EVAL(SetDlgItemText(hdlg, IDD_NAME, pszFileName));

     TRACE_OUT(("SetISPSFileNameAndIcon(): Set property sheet file name to \"%s\".",
            pszFileName));

     SetISPSIcon(hdlg, shfi.hIcon);
      }
      else
      {
     hr = E_FAIL;

     TRACE_OUT(("SetISPSFileNameAndIcon(): SHGetFileInfo() failed, returning %lu.",
            dwResult));
      }
   }
   else
      TRACE_OUT(("SetISPSFileNameAndIcon(): GetCurFile() failed, returning %s.",
         GetHRESULTString(hr)));

   if (hr != S_OK)
      EVAL(SetDlgItemText(hdlg, IDD_NAME, EMPTY_STRING));

   return;
}


PRIVATE_CODE void SetISPSURL(HWND hdlg)
{
   PInternetShortcut pintshcut;
   HRESULT hr;
   PSTR pszURL;

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   pintshcut = ((PISPS)GetWindowLongPtr(hdlg, DWLP_USER))->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   hr = pintshcut->GetURL(&pszURL);

   if (hr == S_OK)
   {
      EVAL(SetDlgItemText(hdlg, IDD_URL, pszURL));

      TRACE_OUT(("SetISPSURL(): Set property sheet URL to \"%s\".",
         pszURL));

      SHFree(pszURL);
      pszURL = NULL;
   }
   else
      EVAL(SetDlgItemText(hdlg, IDD_URL, EMPTY_STRING));

   return;
}


PRIVATE_CODE void SetISPSWorkingDirectory(HWND hdlg)
{
   PInternetShortcut pintshcut;
   HRESULT hr;
   char rgchWorkingDirectory[MAX_PATH_LEN];

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   pintshcut = ((PISPS)GetWindowLongPtr(hdlg, DWLP_USER))->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   hr = pintshcut->GetWorkingDirectory(rgchWorkingDirectory,
                       sizeof(rgchWorkingDirectory));

   if (hr == S_OK)
   {
      EVAL(SetDlgItemText(hdlg, IDD_START_IN, rgchWorkingDirectory));

      TRACE_OUT(("SetISPSWorkingDirectory(): Set property sheet working directory to \"%s\".",
         rgchWorkingDirectory));
   }
   else
   {
      TRACE_OUT(("SetISPSWorkingDirectory(): GetWorkingDirectory() failed, returning %s.",
         GetHRESULTString(hr)));

      EVAL(SetDlgItemText(hdlg, IDD_START_IN, EMPTY_STRING));
   }

   return;
}


PRIVATE_CODE void InitISPSHotkey(HWND hdlg)
{
   PInternetShortcut pintshcut;
   WORD wHotkey;
   HRESULT hr;

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   // Set hotkey combinations.

   SendDlgItemMessage(hdlg, IDD_HOTKEY, HKM_SETRULES,
              (HKCOMB_NONE | HKCOMB_A | HKCOMB_C | HKCOMB_S),
              (HOTKEYF_CONTROL | HOTKEYF_ALT));

   // Set current hotkey.

   pintshcut = ((PISPS)GetWindowLongPtr(hdlg, DWLP_USER))->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   hr = pintshcut->GetHotkey(&wHotkey);
   SendDlgItemMessage(hdlg, IDD_HOTKEY, HKM_SETHOTKEY, wHotkey, 0);

   return;
}


PRIVATE_CODE void InitISPSShowCmds(HWND hdlg)
{
   int niShowCmd;

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   for (niShowCmd = IDS_SHOW_NORMAL;
    niShowCmd <= IDS_SHOW_MAXIMIZED;
    niShowCmd++)
   {
      char rgchShowCmd[s_ucMaxShowCmdLen];

      if (MLLoadStringA(niShowCmd, rgchShowCmd,
             sizeof(rgchShowCmd)))
      {
     SendDlgItemMessage(hdlg, IDD_SHOW_CMD, CB_ADDSTRING, 0,
                (LPARAM)rgchShowCmd);

     TRACE_OUT(("InitISPSShowCmds(): Added show command \"%s\".",
            rgchShowCmd));
      }
      else
     ERROR_OUT(("InitISPSShowCmds(): Unable to load string %d.",
            niShowCmd));
   }

   return;
}


PRIVATE_CODE void SetISPSShowCmd(HWND hdlg)
{
   PInternetShortcut pintshcut;
   int nShowCmd;
   int i;

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   pintshcut = ((PISPS)GetWindowLongPtr(hdlg, DWLP_USER))->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   pintshcut->GetShowCmd(&nShowCmd);

   for (i = 0; i < ARRAY_ELEMENTS(s_rgnShowCmds); i++)
   {
      if (s_rgnShowCmds[i] == nShowCmd)
     break;
   }

   if (i >= ARRAY_ELEMENTS(s_rgnShowCmds))
   {
      ASSERT(i == ARRAY_ELEMENTS(s_rgnShowCmds));

      WARNING_OUT(("SetISPSShowCmd(): Unrecognized show command %d.  Defaulting to normal.",
           nShowCmd));

      i = 0;
   }

   SendDlgItemMessage(hdlg, IDD_SHOW_CMD, CB_SETCURSEL, i, 0);

   TRACE_OUT(("SetISPSShowCmd(): Set property sheet show command to index %d.",
          i));

   return;
}


#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */

PRIVATE_CODE BOOL ISPS_InitDialog(HWND hdlg, WPARAM wparam, LPARAM lparam)
{
   PInternetShortcut pintshcut;

   // wparam may be any value.

   ASSERT(IS_VALID_HANDLE(hdlg, WND));
   ASSERT(IS_VALID_STRUCT_PTR((PCISPS)lparam, CISPS));

   SetWindowLongPtr(hdlg, DWLP_USER, lparam);

   pintshcut = ((PISPS)lparam)->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   // Initialize control contents.

   SetISPSFileNameAndIcon(hdlg);

   SendDlgItemMessage(hdlg, IDD_URL, EM_LIMITTEXT, g_ucMaxURLLen - 1, 0);
   SetISPSURL(hdlg);

   SendDlgItemMessage(hdlg, IDD_START_IN, EM_LIMITTEXT, MAX_PATH_LEN - 1, 0);
   SetISPSWorkingDirectory(hdlg);

   InitISPSHotkey(hdlg);

   InitISPSShowCmds(hdlg);
   SetISPSShowCmd(hdlg);

   return(TRUE);
}


PRIVATE_CODE BOOL ISPS_Destroy(HWND hdlg, WPARAM wparam, LPARAM lparam)
{
   PInternetShortcut pintshcut;

   // wparam may be any value.
   // lparam may be any value.

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   pintshcut = ((PISPS)GetWindowLongPtr(hdlg, DWLP_USER))->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   SetWindowLongPtr(hdlg, DWLP_USER, NULL);

   return(TRUE);
}

#pragma warning(default:4100) /* "unreferenced formal parameter" warning */


PRIVATE_CODE void ISPSChanged(HWND hdlg)
{
   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   PropSheet_Changed(GetParent(hdlg), hdlg);

   return;
}


PRIVATE_CODE HRESULT ChooseIcon(HWND hdlg)
{
   HRESULT hr;
   PISPS pisps;
   PInternetShortcut pintshcut;
   char rgchTempIconFile[MAX_PATH_LEN];
   int niIcon;
   UINT uFlags;

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   pisps = (PISPS)GetWindowLongPtr(hdlg, DWLP_USER);
   ASSERT(IS_VALID_STRUCT_PTR(pisps, CISPS));
   pintshcut = pisps->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   if (pintshcut->GetIconLocation(0, rgchTempIconFile,
                  sizeof(rgchTempIconFile), &niIcon, &uFlags)
       != S_OK)
   {
      rgchTempIconFile[0] = '\0';
      niIcon = 0;
   }

   ASSERT(lstrlen(rgchTempIconFile) < sizeof(rgchTempIconFile));
   if (RUNNING_NT)
   {
        WCHAR uTempIconFile[MAX_PATH_LEN];

        MultiByteToWideChar(CP_ACP, 0, rgchTempIconFile, -1,
                    uTempIconFile, MAX_PATH_LEN);

        if (PickIconDlg(hdlg, uTempIconFile, MAX_PATH_LEN, &niIcon))
        {
            WideCharToMultiByte(CP_ACP, 0, uTempIconFile, -1, pisps->rgchIconFile,
                                 MAX_PATH_LEN, NULL, NULL);

            pisps->niIcon = niIcon;

            hr = S_OK;
        }
        else
        {
            hr = E_FAIL;

            TRACE_OUT(("ChooseIcon(NT): PickIconDlg() failed."));
        }
   }
   else
   {
       if (PickIconDlg(hdlg, (LPWSTR)rgchTempIconFile, sizeof(rgchTempIconFile), &niIcon))  // (LPWSTR) so it builds, but works downlevel
       {
          ASSERT(lstrlen(rgchTempIconFile) < sizeof(pisps->rgchIconFile));
          lstrcpy(pisps->rgchIconFile, rgchTempIconFile);
          pisps->niIcon = niIcon;

          hr = S_OK;
       }
       else
       {
          hr = E_FAIL;

          TRACE_OUT(("ChooseIcon(): PickIconDlg() failed."));
       }
   }

   return(hr);
}


PRIVATE_CODE void UpdateISPSIcon(HWND hdlg)
{
   PISPS pisps;
   HICON hicon;

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   pisps = (PISPS)GetWindowLongPtr(hdlg, DWLP_USER);
   ASSERT(IS_VALID_STRUCT_PTR(pisps, CISPS));
   ASSERT(pisps->rgchIconFile[0]);

   // This icon does not have the link arrow overlayed.  shell32.dll's
   // Shortcut property sheet has the same bug.

   hicon = ExtractIcon(GetThisModulesHandle(), pisps->rgchIconFile,
               pisps->niIcon);

   if (hicon)
      SetISPSIcon(hdlg, hicon);
   else
      WARNING_OUT(("UpdateISPSIcon(): ExtractIcon() failed for icon %d in file %s.",
           pisps->niIcon,
           pisps->rgchIconFile));

   return;
}


#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */

PRIVATE_CODE BOOL ISPS_Command(HWND hdlg, WPARAM wparam, LPARAM lparam)
{
   BOOL bMsgHandled = FALSE;
   WORD wCmd;

   // wparam may be any value.
   // lparam may be any value.

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   wCmd = HIWORD(wparam);

   switch (LOWORD(wparam))
   {
      case IDD_URL:
      case IDD_HOTKEY:
      case IDD_START_IN:
     if (wCmd == EN_CHANGE)
     {
        ISPSChanged(hdlg);

        bMsgHandled = TRUE;
     }
     break;

      case IDD_SHOW_CMD:
     if (wCmd == LBN_SELCHANGE)
     {
        ISPSChanged(hdlg);

        bMsgHandled = TRUE;
     }
     break;

      case IDD_CHANGE_ICON:
     // Ignore return value.
     if (ChooseIcon(hdlg) == S_OK)
     {
        UpdateISPSIcon(hdlg);
        ISPSChanged(hdlg);
     }
     bMsgHandled = TRUE;
     break;

      default:
     break;
   }

   return(bMsgHandled);
}

#pragma warning(default:4100) /* "unreferenced formal parameter" warning */


PRIVATE_CODE HRESULT ComplainAboutURL(HWND hwndParent, PCSTR pcszURL,
                      HRESULT hrError)
{
   HRESULT hr;
   int nResult;

   // Validate hrError below.

   ASSERT(IS_VALID_HANDLE(hwndParent, WND));
   ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR));

   switch (hrError)
   {
      case URL_E_UNREGISTERED_PROTOCOL:
      {
     PSTR pszProtocol;

     hr = CopyURLProtocol(pcszURL, &pszProtocol);

     if (hr == S_OK)
     {
        if (MyMsgBox(hwndParent, MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
             MAKEINTRESOURCE(IDS_UNREGISTERED_PROTOCOL),
             (MB_YESNO | MB_DEFBUTTON2 | MB_ICONEXCLAMATION),
             &nResult, pszProtocol))
        {
           switch (nResult)
           {
          case IDYES:
             hr = S_OK;
             TRACE_OUT(("ComplainAboutURL(): Allowing URL %s despite unregistered protocol %s, by request.",
                pcszURL,
                pszProtocol));
             break;

          default:
             ASSERT(nResult == IDNO);
             hr = E_FAIL;
             TRACE_OUT(("ComplainAboutURL(): Not allowing URL %s because of unregistered protocol %s, as directed.",
                pcszURL,
                pszProtocol));
             break;
           }
        }

        delete pszProtocol;
        pszProtocol = NULL;
     }

     break;
      }

      default:
     ASSERT(hrError == URL_E_INVALID_SYNTAX);

     if (MyMsgBox(hwndParent, MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
              MAKEINTRESOURCE(IDS_INVALID_URL_SYNTAX),
              (MB_OK | MB_ICONEXCLAMATION), &nResult, pcszURL)) {
        ASSERT(nResult == IDOK);
     }

     hr = E_FAIL;

     TRACE_OUT(("ComplainAboutURL(): Not allowing URL %s because of invalid syntax.",
            pcszURL));

     break;
   }

   return(hr);
}


#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */

PRIVATE_CODE HRESULT ComplainAboutWorkingDirectory(HWND hwndParent,
                           PCSTR pcszWorkingDirectory,
                           HRESULT hrError)
{
   int nResult;

   ASSERT(IS_VALID_HANDLE(hwndParent, WND));
   ASSERT(IS_VALID_STRING_PTR(pcszWorkingDirectory, CSTR));
   ASSERT(hrError == E_PATH_NOT_FOUND);

   if (MyMsgBox(hwndParent, MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE),
        MAKEINTRESOURCE(IDS_WORKING_DIR_NOT_FOUND),
        (MB_OK | MB_ICONEXCLAMATION), &nResult, pcszWorkingDirectory)) {
      ASSERT(nResult == IDOK);
   }

   TRACE_OUT(("ComplainAboutWorkingDirectory(): Not allowing non-existent working directory %s.",
          pcszWorkingDirectory));

   return(E_FAIL);
}

#pragma warning(default:4100) /* "unreferenced formal parameter" warning */


PRIVATE_CODE HRESULT InjectISPSData(HWND hdlg)
{
   HRESULT hr;
   PISPS pisps;
   PInternetShortcut pintshcut;
   PSTR pszURL;

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   pisps = (PISPS)GetWindowLongPtr(hdlg, DWLP_USER);
   ASSERT(IS_VALID_STRUCT_PTR(pisps, CISPS));
   pintshcut = pisps->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   hr = CopyDlgItemText(hdlg, IDD_URL, &pszURL);

   if (SUCCEEDED(hr))
   {
      PCSTR pcszURLToUse;
      PSTR pszTranslatedURL;

      pcszURLToUse = pszURL;

      if (hr == S_OK)
      {
     hr = TranslateURL(pszURL, (TRANSLATEURL_FL_GUESS_PROTOCOL |
                    TRANSLATEURL_FL_USE_DEFAULT_PROTOCOL),
               &pszTranslatedURL);

     if (SUCCEEDED(hr))
     {
        if (hr == S_OK)
           pcszURLToUse = pszTranslatedURL;
        else
           ASSERT(hr == S_FALSE);

        hr = ValidateURL(pcszURLToUse);

        if (FAILED(hr))
        {
           hr = ComplainAboutURL(hdlg, pcszURLToUse, hr);

           if (FAILED(hr))
          SetEditFocus(GetDlgItem(hdlg, IDD_URL));
        }
     }
      }
      else
      {
     // A blank URL is OK.

     ASSERT(hr == S_FALSE);

     pszTranslatedURL = NULL;
      }

      if (SUCCEEDED(hr))
      {
     hr = pintshcut->SetURL(pcszURLToUse, 0);

     if (hr == S_OK)
     {
        WORD wHotkey;
        WORD wOldHotkey;

        // Refresh URL in case it was changed by TranslateURL().

        SetISPSURL(hdlg);

        wHotkey = (WORD)SendDlgItemMessage(hdlg, IDD_HOTKEY, HKM_GETHOTKEY,
                           0, 0);

        hr = pintshcut->GetHotkey(&wOldHotkey);

        if (hr == S_OK)
        {
           hr = pintshcut->SetHotkey(wHotkey);

           if (hr == S_OK)
           {
          char szFile[MAX_PATH_LEN];

          hr = pintshcut->GetCurFile(szFile, sizeof(szFile));

          if (hr == S_OK)
          {
             if (RegisterGlobalHotkey(wOldHotkey, wHotkey, szFile))
             {
            PSTR pszWorkingDirectory;

            hr = CopyDlgItemText(hdlg, IDD_START_IN,
                         &pszWorkingDirectory);

            if (SUCCEEDED(hr))
            {
               if (hr == S_OK)
               {
                  hr = ValidateWorkingDirectory(pszWorkingDirectory);

                  if (FAILED(hr))
                 hr = ComplainAboutWorkingDirectory(hdlg,
                                    pszWorkingDirectory,
                                    hr);

                  if (FAILED(hr))
                 SetEditFocus(GetDlgItem(hdlg, IDD_START_IN));
               }

               if (SUCCEEDED(hr))
               {
                  hr = pintshcut->SetWorkingDirectory(pszWorkingDirectory);

                  if (hr == S_OK)
                  {
                 // Refresh working directory in case it was changed by
                 // SetWorkingDirectory().

                 SetISPSWorkingDirectory(hdlg);

                 if (pisps->rgchIconFile[0])
                    hr = pintshcut->SetIconLocation(pisps->rgchIconFile,
                                    pisps->niIcon);

                 if (hr == S_OK)
                 {
                    INT_PTR iShowCmd;

                    iShowCmd = SendDlgItemMessage(hdlg,
                                  IDD_SHOW_CMD,
                                  CB_GETCURSEL,
                                  0, 0);

                    if (iShowCmd >= 0 &&
                    iShowCmd < ARRAY_ELEMENTS(s_rgnShowCmds))
                       pintshcut->SetShowCmd(s_rgnShowCmds[iShowCmd]);
                    else
                       hr = E_UNEXPECTED;
                 }
                  }
               }

               if (pszWorkingDirectory)
               {
                  delete pszWorkingDirectory;
                  pszWorkingDirectory = NULL;
               }
            }
             }
             else
            hr = E_FAIL;
          }
           }
        }
     }
      }

      if (pszURL)
      {
     delete pszURL;
     pszURL = NULL;
      }

      if (pszTranslatedURL)
      {
     LocalFree(pszTranslatedURL);
     pszTranslatedURL = NULL;
      }
   }

   if (hr == S_OK)
      TRACE_OUT(("InjectISPSData(): Injected property sheet data into Internet Shortcut successfully."));
   else
      WARNING_OUT(("InjectISPSData(): Failed to inject property sheet data into Internet Shortcut, returning %s.",
           GetHRESULTString(hr)));

   return(hr);
}


PRIVATE_CODE HRESULT ISPSSave(HWND hdlg)
{
   HRESULT hr;
   PInternetShortcut pintshcut;

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   pintshcut = ((PISPS)GetWindowLongPtr(hdlg, DWLP_USER))->pintshcut;
   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));

   if (pintshcut->IsDirty() == S_OK)
   {
      hr = pintshcut->Save((LPCOLESTR)NULL, FALSE);

      if (hr == S_OK)
     TRACE_OUT(("ISPSSave(): Saved Internet Shortcut successfully."));
      else
     WARNING_OUT(("ISPSSave(): Save() failed, returning %s.",
              GetHRESULTString(hr)));
   }
   else
   {
      TRACE_OUT(("ISPSSave(): Internet Shortcut unchanged.  No save required."));

      hr = S_OK;
   }

   return(hr);
}


#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */

PRIVATE_CODE BOOL ISPS_Notify(HWND hdlg, WPARAM wparam, LPARAM lparam)
{
   BOOL bMsgHandled = FALSE;

   // wparam may be any value.
   // lparam may be any value.

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   switch (((PNMHDR)lparam)->code)
   {
      case PSN_APPLY:
     SetWindowLongPtr(hdlg, DWLP_MSGRESULT, ISPSSave(hdlg) == S_OK ?
                        PSNRET_NOERROR :
                        PSNRET_INVALID_NOCHANGEPAGE);
     bMsgHandled = TRUE;
     break;

      case PSN_KILLACTIVE:
     SetWindowLongPtr(hdlg, DWLP_MSGRESULT, FAILED(InjectISPSData(hdlg)));
     bMsgHandled = TRUE;
     break;

      default:
     break;
   }

   return(bMsgHandled);
}

#pragma warning(default:4100) /* "unreferenced formal parameter" warning */


PRIVATE_CODE PCSTR ISPS_GetHelpFileFromControl(HWND hwndControl)
{
   PCSTR pcszHelpFile = NULL;
   int nControlID = 0;

   ASSERT(! hwndControl ||
      IS_VALID_HANDLE(hwndControl, WND));

   if (hwndControl)
   {
      nControlID = GetDlgCtrlID(hwndControl);

      switch (nControlID)
      {
     case IDD_URL_TEXT:
     case IDD_URL:
        // URL help comes from the Plus! Pack help file.
        pcszHelpFile = s_cszPlusHelpFile;
        break;

     default:
        // Other help is borrowed from the default Win95 help file.
        break;
      }
   }

   TRACE_OUT(("ISPS_GetHelpFileFromControl(): Using %s for control %d (HWND %#lx).",
          pcszHelpFile ? pcszHelpFile : "default Win95 help file",
          nControlID,
          hwndControl));

   ASSERT(! pcszHelpFile ||
      IS_VALID_STRING_PTR(pcszHelpFile, CSTR));

   return(pcszHelpFile);
}


PRIVATE_CODE INT_PTR CALLBACK ISPS_DlgProc(HWND hdlg, UINT uMsg, WPARAM wparam,
                    LPARAM lparam)
{
   INT_PTR bMsgHandled = FALSE;

   // uMsg may be any value.
   // wparam may be any value.
   // lparam may be any value.

   ASSERT(IS_VALID_HANDLE(hdlg, WND));

   switch (uMsg)
   {
      case WM_INITDIALOG:
     bMsgHandled = ISPS_InitDialog(hdlg, wparam, lparam);
     break;

      case WM_DESTROY:
     bMsgHandled = ISPS_Destroy(hdlg, wparam, lparam);
     break;

      case WM_COMMAND:
     bMsgHandled = ISPS_Command(hdlg, wparam, lparam);
     break;

      case WM_NOTIFY:
     bMsgHandled = ISPS_Notify(hdlg, wparam, lparam);
     break;

      case WM_HELP:
         SHWinHelpOnDemandWrap((HWND)(((LPHELPINFO)lparam)->hItemHandle),
         ISPS_GetHelpFileFromControl((HWND)(((LPHELPINFO)lparam)->hItemHandle)),
         HELP_WM_HELP, (DWORD_PTR)(PVOID)s_rgdwHelpIDs);
     bMsgHandled = TRUE;
     break;

      case WM_CONTEXTMENU:
      {
     POINT pt;

     LPARAM_TO_POINT(lparam, pt);
     EVAL(ScreenToClient(hdlg, &pt));

         SHWinHelpOnDemandWrap((HWND)wparam,
         ISPS_GetHelpFileFromControl(ChildWindowFromPoint(hdlg, pt)),
         HELP_CONTEXTMENU, (DWORD_PTR)(PVOID)s_rgdwHelpIDs);
     bMsgHandled = TRUE;
     break;
      }

      default:
    break;
   }

   return(bMsgHandled);
}


PRIVATE_CODE HRESULT AddISPS(PInternetShortcut pintshcut,
                 LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lparam)
{
   HRESULT hr;
   ISPS isps;
   HPROPSHEETPAGE hpsp;

   // lparam may be any value.

   ASSERT(IS_VALID_STRUCT_PTR(pintshcut, CInternetShortcut));
   ASSERT(IS_VALID_CODE_PTR(pfnAddPage, LPFNADDPROPSHEETPAGE));

   ZeroMemory(&isps, sizeof(isps));

   isps.psp.dwSize = sizeof(isps);
   isps.psp.dwFlags = (PSP_DEFAULT | PSP_USECALLBACK);
   isps.psp.hInstance = MLGetHinst();
   isps.psp.pszTemplate = MAKEINTRESOURCE(DLG_INTERNET_SHORTCUT_PROP_SHEET);
   isps.psp.pfnDlgProc = &ISPS_DlgProc;
   isps.psp.pfnCallback = &ISPSCallback;

   isps.pintshcut = pintshcut;

   ASSERT(IS_VALID_STRUCT_PTR(&isps, CISPS));

   hpsp = CreatePropertySheetPage(&(isps.psp));

   if (hpsp)
   {
      if ((*pfnAddPage)(hpsp, lparam))
      {
     pintshcut->AddRef();
     hr = S_OK;
     TRACE_OUT(("AddISPS(): Added Internet Shortcut property sheet."));
      }
      else
      {
     DestroyPropertySheetPage(hpsp);

     hr = E_FAIL;
     WARNING_OUT(("AddISPS(): Callback to add property sheet failed."));
      }
   }
   else
      hr = E_OUTOFMEMORY;

   return(hr);
}


/****************************** Public Functions *****************************/


PUBLIC_CODE void SetEditFocus(HWND hwnd)
{
   ASSERT(IS_VALID_HANDLE(hwnd, WND));

   SetFocus(hwnd);
   SendMessage(hwnd, EM_SETSEL, 0, -1);

   return;
}


PUBLIC_CODE BOOL ConstructMessageString(PCSTR pcszFormat, PSTR *ppszMsg,
                    va_list *ArgList)
{
   BOOL bResult;
   char rgchFormat[MAX_MSG_LEN];

   // ArgList may be any value.

   ASSERT(! HIWORD(pcszFormat) ||
      IS_VALID_STRING_PTR(pcszFormat, CSTR));
   ASSERT(IS_VALID_WRITE_PTR(ppszMsg, PSTR));

   if (IS_INTRESOURCE(pcszFormat))
   {
      MLLoadStringA(LOWORD(PtrToUlong(pcszFormat)), rgchFormat,
         sizeof(rgchFormat));

      pcszFormat = rgchFormat;
   }

   bResult = FormatMessage((FORMAT_MESSAGE_ALLOCATE_BUFFER |
                FORMAT_MESSAGE_FROM_STRING), pcszFormat, 0, 0,
               (PSTR)ppszMsg, 0, ArgList);

   ASSERT(! bResult ||
      IS_VALID_STRING_PTR(*ppszMsg, STR));

   return(bResult);
}


PUBLIC_CODE BOOL __cdecl MyMsgBox(HWND hwndParent, PCSTR pcszTitle,
                  PCSTR pcszMsgFormat, DWORD dwMsgBoxFlags,
                  PINT pnResult, ...)
{
   BOOL bResult;
   va_list ArgList;
   PSTR pszMsg;

   ASSERT(IS_VALID_HANDLE(hwndParent, WND));
   ASSERT(IS_INTRESOURCE(pcszTitle) ||
      IS_VALID_STRING_PTR(pcszTitle, CSTR));
   ASSERT(IS_INTRESOURCE(pcszMsgFormat) ||
      IS_VALID_STRING_PTR(pcszMsgFormat, CSTR));
   ASSERT(FLAGS_ARE_VALID(dwMsgBoxFlags, ALL_MSG_BOX_FLAGS));
   ASSERT(IS_VALID_WRITE_PTR(pnResult, INT));

   *pnResult = 0;

   va_start(ArgList, pnResult);

   bResult = ConstructMessageString(pcszMsgFormat, &pszMsg, &ArgList);

   va_end(ArgList);

   if (bResult)
   {
      char rgchTitle[MAX_MSG_LEN];

      if (! HIWORD(pcszTitle))
      {
          MLLoadStringA(LOWORD(PtrToUlong(pcszTitle)), rgchTitle,
            sizeof(rgchTitle));

          pcszTitle = rgchTitle;
      }

      *pnResult = MessageBox(hwndParent, pszMsg, pcszTitle, dwMsgBoxFlags);

      bResult = (*pnResult != 0);

      LocalFree(pszMsg);
      pszMsg = NULL;
   }

   return(bResult);
}


PUBLIC_CODE HRESULT CopyDlgItemText(HWND hdlg, int nControlID, PSTR *ppszText)
{
   HRESULT hr;
   HWND hwndControl;

   // nContolID may be any value.

   ASSERT(IS_VALID_HANDLE(hdlg, WND));
   ASSERT(IS_VALID_WRITE_PTR(ppszText, PSTR));

   *ppszText = NULL;

   hwndControl = GetDlgItem(hdlg, nControlID);

   if (hwndControl)
   {
      int ncchTextLen;

      ncchTextLen = GetWindowTextLength(hwndControl);

      if (ncchTextLen > 0)
      {
     PSTR pszText;

     ASSERT(ncchTextLen < INT_MAX);
     ncchTextLen++;
     ASSERT(ncchTextLen > 0);

     pszText = new(char[ncchTextLen]);

     if (pszText)
     {
        int ncchCopiedLen;

        ncchCopiedLen = GetWindowText(hwndControl, pszText, ncchTextLen);
        ASSERT(ncchCopiedLen == ncchTextLen - 1);

        if (EVAL(ncchCopiedLen > 0))
        {
           if (AnyMeat(pszText))
           {
          *ppszText = pszText;

          hr = S_OK;
           }
           else
          hr = S_FALSE;
        }
        else
           hr = E_FAIL;

        if (hr != S_OK)
        {
           delete pszText;
           pszText = NULL;
        }
     }
     else
        hr = E_OUTOFMEMORY;
      }
      else
     // No text.
     hr = S_FALSE;
   }
   else
      hr = E_FAIL;

   return(hr);
}


PUBLIC_CODE BOOL RegisterGlobalHotkey(WORD wOldHotkey, WORD wNewHotkey,
                      PCSTR pcszPath)
{
   BOOL bResult;
   HWND hwndTray;

   ASSERT(! wOldHotkey ||
      IsValidHotkey(wOldHotkey));
   ASSERT(! wNewHotkey ||
      IsValidHotkey(wNewHotkey));
   ASSERT(IsValidPath(pcszPath));

   hwndTray = FindWindow(s_cszTrayNotificationClass, 0);

   if (hwndTray)
   {
      if (wOldHotkey)
      {
          SendMessage(hwndTray, WMTRAY_SCUNREGISTERHOTKEY, wOldHotkey, 0);

          TRACE_OUT(("RegisterGlobalHotkey(): Unregistered old hotkey %#04x for %s.",
              wOldHotkey,
              pcszPath));
      }

      if (wNewHotkey)
      {
        if (RUNNING_NT)
        {
            ATOM atom = GlobalAddAtom(pcszPath);
            ASSERT(atom);
            if (atom)
            {
                SendMessage(hwndTray, WMTRAY_SCREGISTERHOTKEY, wNewHotkey, (LPARAM)atom);
                GlobalDeleteAtom(atom);
            }
        }
        else
        {
            SendMessage(hwndTray, WMTRAY_SCREGISTERHOTKEY, wNewHotkey,
                        (LPARAM)pcszPath);
        }

          TRACE_OUT(("RegisterGlobalHotkey(): Registered new hotkey %#04x for %s.",
              wNewHotkey,
              pcszPath));
      }

      bResult = TRUE;
   }
   else
   {
      bResult = FALSE;

      WARNING_OUT(("RegisterGlobalHotkey(): Unable to find Tray window of class %s to notify.",
           s_cszTrayNotificationClass));
   }

   return(bResult);
}

/********************************** Methods **********************************/


#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */

HRESULT STDMETHODCALLTYPE InternetShortcut::Initialize(
                              PCITEMIDLIST pcidlFolder,
                              PIDataObject pido,
                              HKEY hkeyProgID)
{
   HRESULT hr;
   FORMATETC fmtetc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
   STGMEDIUM stgmed;

   DebugEntry(InternetShortcut::Initialize);

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));
   ASSERT(! pcidlFolder ||
      IS_VALID_STRUCT_PTR(pcidlFolder, CITEMIDLIST));
   ASSERT(IS_VALID_INTERFACE_PTR(pido, IDataObject));
   ASSERT(IS_VALID_HANDLE(hkeyProgID, KEY));

   hr = pido->GetData(&fmtetc, &stgmed);

   if (hr == S_OK)
   {
      UINT ucbPathLen;
      PSTR pszFile;

      // (+ 1) for null terminator.

      ucbPathLen = DragQueryFile((HDROP)(stgmed.hGlobal), 0, NULL, 0) + 1;
      ASSERT(ucbPathLen > 0);

      pszFile = new(char[ucbPathLen]);

      if (pszFile)
      {
     EVAL(DragQueryFile((HDROP)(stgmed.hGlobal), 0, pszFile, ucbPathLen) == ucbPathLen - 1);
     ASSERT(IS_VALID_STRING_PTR(pszFile, STR));
     ASSERT((UINT)lstrlen(pszFile) == ucbPathLen - 1);

     hr = LoadFromFile(pszFile, TRUE);

     delete pszFile;
     pszFile = NULL;
      }
      else
     hr = E_OUTOFMEMORY;

      if (stgmed.tymed != TYMED_NULL)
         MyReleaseStgMedium(&stgmed);
   }

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));

   DebugExitHRESULT(InternetShortcut::Initialize, hr);

   return(hr);
}

#pragma warning(default:4100) /* "unreferenced formal parameter" warning */


HRESULT STDMETHODCALLTYPE InternetShortcut::AddPages(
                         LPFNADDPROPSHEETPAGE pfnAddPage,
                         LPARAM lparam)
{
   HRESULT hr;

   DebugEntry(InternetShortcut::AddPages);

   // lparam may be any value.

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));
   ASSERT(IS_VALID_CODE_PTR(pfnAddPage, LPFNADDPROPSHEETPAGE));

   hr = AddISPS(this, pfnAddPage, lparam);

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));

   DebugExitHRESULT(InternetShortcut::AddPages, hr);

   return(hr);
}


#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */

HRESULT STDMETHODCALLTYPE InternetShortcut::ReplacePage(
                      UINT uPageID,
                      LPFNADDPROPSHEETPAGE pfnReplaceWith,
                      LPARAM lparam)
{
   HRESULT hr;

   DebugEntry(InternetShortcut::ReplacePage);

   // lparam may be any value.
   // uPageID may be any value.

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));
   ASSERT(IS_VALID_CODE_PTR(pfnReplaceWith, LPFNADDPROPSHEETPAGE));

   hr = E_NOTIMPL;

   ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut));

   DebugExitHRESULT(InternetShortcut::ReplacePage, hr);

   return(hr);
}

#pragma warning(default:4100) /* "unreferenced formal parameter" warning */
