/*
 * pmcomman.c - program manager
 *
 *  Copyright (c) 1991,  Microsoft Corporation
 *
 *  DESCRIPTION
 *
 *                This file is for support of program manager under NT Windows.
 *                This file is/was ported from pmcomman.c (program manager).
 *
 *  MODIFICATION HISTORY
 *      Initial Version: x/x/90        Author Unknown, since he didn't feel
 *                                                                like commenting the code...
 *
 *      NT 32b Version:         1/22/91        Jeff Pack   Intitial port to begin.
 *                              6/15/91        JohanneC    re-ported.
 *
 */

#include "progman.h"

/*** IsRemoteDrive -- tests to see if drive is a remote drive
 *
 *
 * BOOL APIENTRY IsRemoteDrive(wDrive)
 *
 * ENTRY -         int                wDrive - drive number to test
 *
 * EXIT  -        BOOL        xxx -  returns TRUE if remote, false if not.
 *
 * SYNOPSIS -  calls GetDriveType to determine if media is remote or not.
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

BOOL APIENTRY IsRemoteDrive(int wDrive)
{

  TCHAR         pszdrive[5] = TEXT("c:\\");       /*default string value*/

  pszdrive[0] = (TCHAR)wDrive + (TCHAR)TEXT('A');  /*convert wDrive (0-25) to drive letter*/
                                           /*and place in string to pass to GetDriveType*/

  return((BOOL) (GetDriveType(pszdrive) == DRIVE_REMOTE));
}


/*** IsRemovableDrive -- tests to see if drive is removable
 *
 *
 * BOOL APIENTRY IsRemovableDrive( int wDrive)
 *
 * ENTRY -         int                wDrive - drive number to test
 *
 * EXIT  -        BOOL        xxx -  returns TRUE if removable, false if not.
 *
 * SYNOPSIS -  calls GetDriveType to determine if media is removable or not.
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

BOOL APIENTRY IsRemovableDrive( int wDrive)
{
  TCHAR         pszdrive[5] = TEXT("c:\\");                /*default string value*/


  pszdrive[0] = (TCHAR)wDrive + (TCHAR)TEXT('A');                /*convert wDrive (0-25) to drive letter*/
                                                                        /*and place in string to pass to GetDriveType*/
  return((BOOL)(GetDriveType(pszdrive) == DRIVE_REMOVABLE));
}

/*** BuildDescription --
 *
 *
 * VOID APIENTRY BuildDescription(LPTSTR szName, LPTSTR szPath)
 *
 * ENTRY -         LPTSTR        szName
 *                 LPTSTR        szPath
 *
 * EXIT  -        VOID        xxx -
 *
 * SYNOPSIS -
 *
 * WARNINGS -  sordid coding style  BUG BUG assumes 8.3 filename convention
 * EFFECTS  -
 *
 */

VOID APIENTRY BuildDescription(LPTSTR szName, LPTSTR szPath)
{
  TCHAR      ch;
  TCHAR      ch2 = 0;
  LPTSTR     p;
  LPTSTR     p2;
  LPTSTR     p3  = NULL;

//When User creating new icon with command line added quote (such as "a b.exe")
// and no description, then invalid description ("a b) added new icon.

  BOOL      bQuote = FALSE;

  if (*szPath == TEXT('"') && *(szPath+lstrlen(szPath)-1) == TEXT('"')) {
      bQuote = TRUE;
      *(szPath+lstrlen(szPath)-1) = TEXT('\0');
      szPath++;
  }

  p = p2 = szPath;

  /* Scan the string looking for the last filename. */
  while (*p) {
      if (*p == TEXT('\\'))
          p2 = p+1;
      else if (*p == TEXT('.'))
          p3 = p;
      p = CharNext(p);
  }

  if (!p3)
      p3 = p;

  ch = *p3;
  *p3 = TEXT('\0');

  if (lstrlen(p2) > MAXITEMNAMELEN) {
      ch2 = *(p2 + MAXITEMNAMELEN);
      *(p2 + MAXITEMNAMELEN) = TEXT('\0');
  }

  lstrcpy(szName, p2);
  *p3 = ch;

  if (ch2) {
      *(p2 + MAXITEMNAMELEN) = ch2;
  }

  if( bQuote )
    *(szPath+lstrlen(szPath)) = TEXT('"');

  CharUpper(szName);
  CharLower(CharNext(szName));
}

/* Returns 0 for success.  Otherwise returns a IDS_ string code. */

/*** ExecProgram -- exec program function
 *
 *
 * WORD APIENTRY ExecProgram(LPTSTR lpszPath, LPTSTR lpDir, LPTSTR lpTitle, BOOL bLoadIt)
 *
 * ENTRY -        LPTSTR      lpszPath    -
 *                LPTSTR      lpDir       -
 *                BOOL        bLoadIt     -
 *
 * EXIT  -        BOOL        xxx         -         returns (0)FALSE if successful, else returns
 *                                                                IDS_ string code.
 *
 * SYNOPSIS -
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

WORD APIENTRY ExecProgram (
    LPTSTR lpszPath,
    LPTSTR lpDir,
    LPTSTR lpTitle,
    BOOL bLoadIt,
    DWORD dwDDEId,
    WORD wHotKeyId,
    BOOL bNewVDM
    )
{
  WORD      ret;
  WORD      wNTVDMFlags=0;
  HCURSOR   hCursor;
  LPTSTR     lpP;
  TCHAR cSeparator;
  TCHAR lpReservedFormat[] = TEXT("dde.%d,hotkey.%d,ntvdm.%d");
  TCHAR lpReserved[100];  // used for DDE request of icons from console apps
                         // add for passing the hotkey associated with an item.
  DWORD OldErrorMode;

  ret = 0;
  //hPendingWindow = NULL;

  hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

  /* Don't mess with the mouse state; unless we're on a mouseless system.
   */
  if (!GetSystemMetrics(SM_MOUSEPRESENT))
      ShowCursor(TRUE);

  /* skip leading spaces
   */
  while (*lpszPath == TEXT(' '))
      lpszPath++;

  /* skip past path
   */
  lpP = lpszPath;
  if (*lpszPath == TEXT('"')) {
     cSeparator = TEXT('"');
     lpP++;
  }
  else {
     cSeparator = TEXT(' ');
  }

  for (; *lpP && *lpP != cSeparator; lpP = CharNext(lpP))
      ;
  if (*lpP == TEXT('"')) {
     lpP++;
  }

  /* if stuff on end, separate it
   */
  if (*lpP)
      *lpP++ = 0;

  /* Try to exec 'szCommandLine'. */
  fInExec = TRUE;

  /*changed order, since wPendINstance is a 32b HANDLE, and ret is WORD*/
    if (!lpP)
        lpP = TEXT("");

    // Setup this flags variable so NTVDM can overwrite pif information
    // if user has specified info in the icon properties.

    if (lpDir && *lpDir)
       wNTVDMFlags |= PROPERTY_HAS_CURDIR;

    if (wHotKeyId)
       wNTVDMFlags |= PROPERTY_HAS_HOTKEY;

    if (lpTitle && *lpTitle)
       wNTVDMFlags |= PROPERTY_HAS_TITLE;

    wsprintf(lpReserved, lpReservedFormat, dwDDEId, wHotKeyId, wNTVDMFlags);

    OldErrorMode = SetErrorMode(0);
    ret = (WORD)RealShellExecuteEx(hwndProgman, NULL, lpszPath, lpP,
                            lpDir, NULL, lpTitle, lpReserved,
                            (WORD)(bLoadIt ? SW_SHOWMINNOACTIVE : SW_SHOWNORMAL), NULL,
                            bNewVDM ? EXEC_SEPARATE_VDM : 0);
    SetErrorMode(OldErrorMode);

  fInExec = FALSE;

   // Unfortunately we are still using the 0 - 31 error code
   // combinations instead of the NT error codes.  SE_ERR_OOM
   // is the default case for all errors in RealShellExecuteExW,
   // thus displaying an out of memory error is probably bogus.
   // We can call GetLastError ourselves to get the _real_ error.
   //
   // Special cases:
   //
   // 1)  If you have a really deep directory structure(70 chars), and you
   //     try to spawn a WOW app from it CreateProcess will fail with
   //     and error of ERROR_INVALID_PARAMETER.  We'll grab this case,
   //     and map it into the bad path message since it is the closest.

   if (ret == SE_ERR_OOM)
      {
      DWORD dwResult = GetLastError();

      if (dwResult == ERROR_INVALID_PARAMETER)
         ret = SE_ERR_PNF;
      }


  /*BUG BUG these are DOS exec function return codes, no map yet to NT return codes!*/
  switch (ret) {
      case 0:
      case SE_ERR_OOM:    // 8
          ret = IDS_NOMEMORYMSG;
          break;

      case SE_ERR_FNF:    // 2
          ret = IDS_FILENOTFOUNDMSG;
          break;

      case SE_ERR_PNF:    // 3
          ret = IDS_BADPATHMSG;
          break;

      case 4:
          ret = IDS_MANYOPENFILESMSG;
          break;

      case 5:
          ret = IDS_ACCESSDENIED;
          break;

      case 10:
          ret = IDS_NEWWINDOWSMSG;
          break;

      case 12:
          ret = IDS_OS2APPMSG;
          break;

      case 15:
          /* KERNEL has already put up a messagebox for this one. */
          ret = 0;
          break;

      case 16:
          ret = IDS_MULTIPLEDSMSG;
          break;

      case 18:
          ret = IDS_PMODEONLYMSG;
          break;

      case 19:
          ret = IDS_COMPRESSEDEXE;
          break;

      case 20:
          ret = IDS_INVALIDDLL;
          break;

      case ERROR_NOT_ENOUGH_QUOTA:
      case STATUS_PAGEFILE_QUOTA:
          ret = IDS_INSUFFICIENTQUOTA;
          break;

      case SE_ERR_SHARE:
          ret = IDS_SHAREERROR;
	  break;

      case SE_ERR_ASSOCINCOMPLETE:
          ret = IDS_ASSOCINCOMPLETE;
	  break;

      case SE_ERR_DDETIMEOUT:
      case SE_ERR_DDEFAIL:
      case SE_ERR_DDEBUSY:
          ret = IDS_DDEFAIL;
          break;

      case SE_ERR_NOASSOC:
          ret = IDS_NOASSOCMSG;
          break;

      default:
          if (ret < 32)
              goto EPExit;

          if (bMinOnRun && !bLoadIt)
              ShowWindow(hwndProgman, SW_SHOWMINNOACTIVE);

          ret = 0;
  }

EPExit:

  if (!GetSystemMetrics(SM_MOUSEPRESENT)) {
      /*
       * We want to turn the mouse off here on mouseless systems, but
       * the mouse will already have been turned off by USER if the
       * app has GP'd so make sure everything's kosher.
       */
      if (ShowCursor(FALSE) != -1)
          ShowCursor(TRUE);
  }

  SetCursor(hCursor);

  return(ret);
}


/*** SelectionType --
 *
 *
 * WORD APIENTRY SelectionType(VOID)
 *
 * ENTRY -         VOID
 *
 * EXIT  -        WORD        xxx         -
 *
 *
 * SYNOPSIS -
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

WORD APIENTRY SelectionType(VOID)
{
  /* If no groups, return GROUP type (so user can create one). */
  if (!pCurrentGroup)
      return(TYPE_PERSGROUP);

  if (IsIconic(pCurrentGroup->hwnd))
      if (pCurrentGroup->fCommon)
          return(TYPE_COMMONGROUP);
      else
          return(TYPE_PERSGROUP);

  return(TYPE_ITEM);
}

/*** ExecItem --
 *
 *
 * VOID APIENTRY ExecItem(PGROUP pGroup, PITEM pItem, BOOL fShift, BOOL fStartup)
 *
 * ENTRY -         PGROUP        pGroup                -
 *                        PITEM        pItem                -
 *
 * EXIT  -        VOID
 *
 *
 * SYNOPSIS -
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */
VOID APIENTRY ExecItem(PGROUP pGroup, PITEM pItem, BOOL fShift, BOOL fStartup)
{
    WORD      ret;
    TCHAR      szCommand[MAXITEMPATHLEN + 1];
    TCHAR      szDir[2*(MAXITEMPATHLEN + 1)];
    TCHAR      szTemp[MAXMESSAGELEN+1];
    TCHAR      *szTitle;
    GROUPDEF  *lpgd;

    //
    // Exec the item in the user's home directory.
    //
    SetCurrentDirectory(szOriginalDirectory);

    GetItemCommand(pGroup,pItem,szCommand,szDir);

    if (fShift) {
        fShift = GetKeyState(VK_SHIFT) < (SHORT)0;
    }

    ret = (WORD)((WORD)fShift || GroupFlag(pGroup, pItem, (WORD)ID_MINIMIZE));
    wPendingHotKey = GroupFlag(pGroup, pItem, (WORD)ID_HOTKEY);

    pExecingGroup = pGroup;
    pExecingItem = pItem;
    DoEnvironmentSubst(szCommand, MAXITEMPATHLEN + 1);
    DoEnvironmentSubst(szDir, 2 * (MAXITEMPATHLEN + 1));

    // REVIEW Check the working directory first because the user may
    // still want to try to run the app even with an invalid working
    // dir.
    // Check working directory.
    GetCurrentDirectory(MAXMESSAGELEN+1, szTemp);

    SheRemoveQuotes(szDir);

    // Allow dir field to be NULL without error.
    if (szDir && *szDir) {
        // NB The VPD call is because SCD sometimes returns success even
        // if the drive is invalid. SCD returns FALSE on success.
        if ((!ValidPathDrive(szDir) ||
		     !SetCurrentDirectory(szDir)) &&
		     !fStartup) {
            if (MyMessageBox(hwndProgman, IDS_BADPATHTITLE, IDS_BADPATHMSG3, NULL, MB_OKCANCEL | MB_DEFBUTTON2 | MB_ICONEXCLAMATION) == IDCANCEL)
	        goto Exit;
        }
    }

    SetCurrentDirectory(szTemp);
    //OemToChar(szDir, szDir);

    if ((lpgd = LockGroup(pGroup->hwnd)) == NULL)
        return;
    szTitle = (TCHAR *)PTR(lpgd, ((LPITEMDEF)ITEM(lpgd, pItem->iItem))->pName);

    if (!dwDDEAppId || (dwDDEAppId != pItem->dwDDEId)) {
        dwDDEAppId++;
    }
    pItem->dwDDEId = dwDDEAppId;

    ret = ExecProgram(szCommand, szDir, szTitle, ret, dwDDEAppId,
                      GroupFlag(pGroup, pItem, ID_HOTKEY),
                      GroupFlag(pGroup, pItem, ID_NEWVDM) );

    UnlockGroup(pGroup->hwnd);

    pExecingGroup = NULL;
    pExecingItem = NULL;

    // Check for errors.
    if (ret) {
        if (fStartup) {
             /*
              * This exec is for an item in the startup group, the error
              * message will need to be patched up by adding a comment
              * after the path bit.
              */
             szTemp[0] = TEXT('\'');
             lstrcpy(&szTemp[1], szCommand);
             LoadString(hAppInstance, IDS_STARTUPERR, szCommand, CharSizeOf(szCommand));
             lstrcat(szTemp, szCommand);
             lstrcpy(szCommand, szTemp);
        }
        MyMessageBox(hwndProgman, IDS_EXECERRTITLE, ret, szCommand, MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
    }
Exit:
  SetCurrentDirectory(szWindowsDirectory);
}

VOID RemoveAnsiGroups()
{
    HMENU hMenu;
    FILETIME ft;
    TCHAR szGroupKey[MAXKEYLEN];
    DWORD cchGroupKey = CharSizeOf(szGroupKey);
    HCURSOR hCursor;
    INT i = 0;

    if (!MyDialogBox(UPDATEGROUPSDLG, hwndProgman, UpdateGroupsDlgProc)) {
        return;
    }

    if (RegOpenKeyEx(HKEY_CURRENT_USER, szAnsiProgramGroups, 0,
                     KEY_READ | DELETE, &hkeyAnsiProgramGroups) != ERROR_SUCCESS) {
        return;
    }

    hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
    ShowCursor(TRUE);

    while (!RegEnumKeyEx(hkeyAnsiProgramGroups, i, szGroupKey, &cchGroupKey, 0, 0, 0, &ft)) {
        if (cchGroupKey) {
            if (RegDeleteKey(hkeyAnsiProgramGroups, szGroupKey))
                i++;
        }
        cchGroupKey = CharSizeOf(szGroupKey);
    }
    RegCloseKey(hkeyAnsiProgramGroups);

    if (!RegDeleteKey(HKEY_CURRENT_USER, szAnsiProgramGroups)) {
        //
        // Change Options menu: remove the 'Update Program Groups' menu item
        //
        if (pCurrentGroup &&
              GetWindowLong(pCurrentGroup->hwnd, GWL_STYLE) & WS_MAXIMIZE) {
            hMenu = GetSubMenu(GetMenu(hwndProgman), 2);
        }
        else {
            hMenu = GetSubMenu(GetMenu(hwndProgman), 1);
        }
        DeleteMenu(hMenu, 6, MF_BYPOSITION);
        DeleteMenu(hMenu, 5, MF_BYPOSITION);
        DrawMenuBar(hwndProgman);
    }
    RegDeleteKey(hkeyProgramManager, TEXT("Groups"));
    RegDeleteValue(hkeyPMSettings, szAnsiOrder);

    ShowCursor(FALSE);
    SetCursor(hCursor);

}

/*** ProgmanCommandProc --
 *
 *
 * BOOL APIENTRY ProgmanCommandProc(register HWND hwnd, WORD wMsg,
 *                                                                register WPARAM wParam, LPARAM lParam)
 *
 * ENTRY -         HWND        hWnd
 *                        WORD        wMsg
 *                        WPARAM        wParam
 *                        LPARAM        lParam
 * EXIT  -        BOOL        xxx - returns info, or zero, for nothing to return
 *
 * SYNOPSIS -  ???
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

BOOL APIENTRY ProgmanCommandProc(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    BOOL  bMaxed;
    WORD  wHelpCmd;
    HMENU hMenu;
    LPTSTR  psz;
    HWND  hwndMdiActive;

    hwndMdiActive = (HWND)SendMessage(hwndMDIClient, WM_MDIGETACTIVE, 0, 0L);
    if (hwndMdiActive != NULL)
        bMaxed = (GetWindowLong(hwndMdiActive, GWL_STYLE) & WS_MAXIMIZE) ? TRUE : FALSE;
    else
        bMaxed = 0;

    dwContext = (DWORD)(IDH_HELPFIRST + wParam);

    switch (GET_WM_COMMAND_ID(wParam, lParam)) {

    case IDM_OPEN:
        if (!pCurrentGroup)
            break;

        if (SelectionType() == TYPE_ITEM)
            ExecItem(pCurrentGroup,pCurrentGroup->pItems,TRUE, FALSE);
        else
            SendMessage(pCurrentGroup->hwnd, WM_SYSCOMMAND, SC_RESTORE, 0L);
        break;

    case IDM_NEW:
        if (fNoFileMenu)
            break;

        if (dwEditLevel == 1)
            goto PCPNewItem;

        if (dwEditLevel > 1)
            break;

        if (MyDialogBox(CHOOSERDLG, hwndProgman, ChooserDlgProc)) {
            switch (wNewSelection) {

            case TYPE_PERSGROUP:
            case TYPE_COMMONGROUP:
                MyDialogBox(GROUPDLG, hwndProgman, NewGroupDlgProc);
                break;

            case TYPE_ITEM:
PCPNewItem:
                /*
                 * We are creating a new program item.
                 */
                MyDialogBox(ITEMDLG, hwndProgman, NewItemDlgProc);
                break;
            }
        }
        break;

    case IDM_MOVE:
        if (fNoFileMenu)
            break;

        MyDialogBox(MOVECOPYDLG, hwndProgman, MoveItemDlgProc);
        break;

    case IDM_COPY:
        if (fNoFileMenu)
            break;

        MyDialogBox(MOVECOPYDLG, hwndProgman, CopyItemDlgProc);
        break;

    case IDM_DELETE:
        if (fNoFileMenu)
            break;

        switch (SelectionType()) {

        case TYPE_ITEM:

            if (pCurrentGroup->pItems) {
                wParam = IDS_CONFIRMDELITEMMSG;
                GetItemText(pCurrentGroup, pCurrentGroup->pItems, szNameField, 0);
                psz = szNameField;
                break;
            }
                /*** FALL THRU ***/

        case TYPE_PERSGROUP:
        case TYPE_COMMONGROUP:

            wParam = IDS_CONFIRMDELGROUPMSG;
            GetWindowText(pCurrentGroup->hwnd, (LPTSTR)szNameField, MAXITEMPATHLEN);
            psz = szNameField;
            break;
        }

        if (MyMessageBox(hwndProgman, (WORD)IDS_CONFIRMDELTITLE, (WORD)wParam,
                psz, (WORD)(MB_YESNO | MB_ICONEXCLAMATION)) == IDYES) {
            if (wParam == (WPARAM)IDS_CONFIRMDELITEMMSG) {
                DeleteItem(pCurrentGroup,pCurrentGroup->pItems);
            } else {
                DeleteGroup(pCurrentGroup->hwnd);
            }
        }
        break;

    case IDM_PROPS:
        if (fNoFileMenu)
            break;

        switch (SelectionType()) {

        case TYPE_ITEM:
            if (pCurrentGroup->pItems) {
                MyDialogBox(ITEMDLG, hwndProgman, EditItemDlgProc);
                break;
            }
            /*** FALL THRU ***/

        case TYPE_PERSGROUP:
        case TYPE_COMMONGROUP:
            {
                LPGROUPDEF lpgd;

                lpgd = LockGroup(pCurrentGroup->hwnd);
                if (lpgd == 0L) {
                    break;
                }

                lstrcpy(szNameField, (LPTSTR) PTR(lpgd, lpgd->pName));
                UnlockGroup(pCurrentGroup->hwnd);
                MyDialogBox(GROUPDLG, hwndProgman, EditGroupDlgProc);
                break;
            }
        }
        break;

    case IDM_RUN:
        if (fNoFileMenu)
            break;

        MyDialogBox(RUNDLG, hwndProgman, RunDlgProc);
        break;

    case IDM_EXIT:
        if (fNoFileMenu)
              break;

        PostMessage(hwndProgman, WM_CLOSE, 0, (LPARAM)-1);
	    break;

    case IDM_SHUTDOWN:
        if (fNoFileMenu)
              break;

        /* Don't close if restricted. */
        if (fNoClose)
            break;

        if (bExitWindows) {

            fExiting = TRUE;
            SetWindowLong (hwndProgman, GWL_EXITING, 1);

            /* Call the ShutdownDialog API. */
            ShutdownDialog(hAppInstance, hwndProgman);

            /* User clicked cancel or some app refused the ExitWindows... */
            fExiting = FALSE;
            SetWindowLong (hwndProgman, GWL_EXITING, 0);
        }
        break;

    case IDM_AUTOARRANGE:
          bAutoArrange = !bAutoArrange;

          /* Check/Uncheck the menu item. */
          hMenu = GetSubMenu(GetMenu(hwndProgman), IDM_OPTIONS + (int)bMaxed - (int)fNoFileMenu);
          CheckMenuItem(hMenu, GET_WM_COMMAND_ID(wParam, lParam),
                (WORD)(bAutoArrange ? MF_CHECKED : MF_UNCHECKED));
          if (hkeyPMSettings)
              RegSetValueEx(hkeyPMSettings, szAutoArrange, 0, REG_DWORD, (LPBYTE)&bAutoArrange, sizeof(bAutoArrange));
          if (bAutoArrange) {
              HWND hwndT;

              for (hwndT=GetWindow(hwndMDIClient, GW_CHILD); hwndT;
                                    hwndT=GetWindow(hwndT, GW_HWNDNEXT)) {
                  if (GetWindow(hwndT, GW_OWNER))
                      continue;

                  ArrangeItems(hwndT);
              }
          }
          break;

    case IDM_MINONRUN:
          bMinOnRun = !bMinOnRun;

          /* Check/Uncheck the menu item. */
          hMenu = GetSubMenu(GetMenu(hwndProgman), IDM_OPTIONS + (int)bMaxed - (int)fNoFileMenu);
          CheckMenuItem(hMenu, GET_WM_COMMAND_ID(wParam, lParam),
                (WORD)(bMinOnRun ? MF_CHECKED : MF_UNCHECKED));
          if (hkeyPMSettings)
              RegSetValueEx(hkeyPMSettings,
                            szMinOnRun,
                            0,
                            REG_DWORD,
                            (LPBYTE)&bMinOnRun,
                            sizeof(bMinOnRun));
          break;

    case IDM_SAVESETTINGS:
          bSaveSettings = !bSaveSettings;

          /* Check/Uncheck the menu item. */
          hMenu = GetSubMenu(GetMenu(hwndProgman), IDM_OPTIONS + (int)bMaxed - (int)fNoFileMenu);
          CheckMenuItem(hMenu, GET_WM_COMMAND_ID(wParam, lParam),
                (UINT)(bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
          if (hkeyPMSettings)
              RegSetValueEx(hkeyPMSettings,
                            szSaveSettings,
                            0,
                            REG_DWORD,
                            (LPBYTE)&bSaveSettings,
                            sizeof(bSaveSettings));
          break;

      case IDM_SAVENOW:
          WriteINIFile();
          break;

      case IDM_ANSIGROUPS:
          RemoveAnsiGroups();
          break;

      case IDM_CASCADE:
          SendMessage(hwndMDIClient, WM_MDICASCADE, 0, 0L);
          break;

      case IDM_TILE:
          SendMessage(hwndMDIClient, WM_MDITILE, 0, 0L);
          break;

      case IDM_ARRANGEICONS:
          if (SelectionType() != TYPE_ITEM)
              SendMessage(hwndMDIClient, WM_MDIICONARRANGE, 0, 0L);
          else
              ArrangeItems(pCurrentGroup->hwnd);
          break;

      case IDM_HELPINDEX:
          wHelpCmd = HELP_INDEX;
          wParam = 0;
          goto ACPCallHelp;

      case IDM_HELPSEARCH:
          wHelpCmd = HELP_PARTIALKEY;
          wParam = (WPARAM)szNULL;
          goto ACPCallHelp;

      case IDM_HELPHELP:
          wHelpCmd = HELP_HELPONHELP;
          wParam = 0;

ACPCallHelp:
          SetCurrentDirectory(szOriginalDirectory);
          if (!WinHelp(hwndProgman, szProgmanHelp, wHelpCmd, (DWORD)wParam)) {
              MyMessageBox(hwndProgman, IDS_APPTITLE, IDS_WINHELPERR, NULL, MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);
          }
          SetCurrentDirectory(szWindowsDirectory);
          break;

      case IDM_ABOUT:
      {
          TCHAR szTitle[40];

          LoadString(hAppInstance, IDS_APPTITLE, szTitle, CharSizeOf(szTitle));
          if (ShellAbout(hwndProgman, szTitle, NULL, NULL) == -1)
              MessageBox(hwndProgman, szOOMExitMsg, szOOMExitTitle, MB_ICONHAND | MB_SYSTEMMODAL | MB_OK);
          break;
      }

      default:
          return(FALSE);
    }

  return(TRUE);

}
