[EXPLORER][SHELL32][USER32] Implement 'Show the Desktop' action of Task Bar (#668)
[reactos.git] / base / shell / explorer / traywnd.cpp
index fefd0f0..8fd6c55 100644 (file)
@@ -2,6 +2,7 @@
  * ReactOS Explorer
  *
  * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
+ * Copyright 2018 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
  *
  * this library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,6 +21,7 @@
 
 #include "precomp.h"
 #include <commoncontrols.h>
+#include <traycmd.h>
 
 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu);
 
@@ -55,6 +57,78 @@ HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, ICont
 
 static const WCHAR szTrayWndClass[] = L"Shell_TrayWnd";
 
+struct EFFECTIVE_INFO
+{
+    HWND hwndFound;
+    HWND hwndDesktop;
+    HWND hwndProgman;
+    HWND hTrayWnd;
+    BOOL bMustBeInMonitor;
+};
+
+static BOOL CALLBACK
+FindEffectiveProc(HWND hwnd, LPARAM lParam)
+{
+    EFFECTIVE_INFO *pei = (EFFECTIVE_INFO *)lParam;
+
+    if (!IsWindowVisible(hwnd) || IsIconic(hwnd))
+        return TRUE;    // continue
+
+    if (pei->hTrayWnd == hwnd || pei->hwndDesktop == hwnd ||
+        pei->hwndProgman == hwnd)
+    {
+        return TRUE;    // continue
+    }
+
+    if (pei->bMustBeInMonitor)
+    {
+        // is the window in the nearest monitor?
+        HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
+        if (hMon)
+        {
+            MONITORINFO info;
+            ZeroMemory(&info, sizeof(info));
+            info.cbSize = sizeof(info);
+            if (GetMonitorInfoW(hMon, &info))
+            {
+                RECT rcWindow, rcMonitor, rcIntersect;
+                rcMonitor = info.rcMonitor;
+
+                GetWindowRect(hwnd, &rcWindow);
+
+                if (!IntersectRect(&rcIntersect, &rcMonitor, &rcWindow))
+                    return TRUE;    // continue
+            }
+        }
+    }
+
+    pei->hwndFound = hwnd;
+    return FALSE;   // stop if found
+}
+
+static BOOL
+IsThereAnyEffectiveWindow(BOOL bMustBeInMonitor)
+{
+    EFFECTIVE_INFO ei;
+    ei.hwndFound = NULL;
+    ei.hwndDesktop = GetDesktopWindow();
+    ei.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
+    ei.hwndProgman = FindWindowW(L"Progman", NULL);
+    ei.bMustBeInMonitor = bMustBeInMonitor;
+
+    EnumWindows(FindEffectiveProc, (LPARAM)&ei);
+    if (ei.hwndFound && FALSE)
+    {
+        WCHAR szClass[64], szText[64];
+        GetClassNameW(ei.hwndFound, szClass, _countof(szClass));
+        GetWindowTextW(ei.hwndFound, szText, _countof(szText));
+        MessageBoxW(NULL, szText, szClass, 0);
+    }
+    return ei.hwndFound != NULL;
+}
+
+CSimpleArray<HWND>  g_MinimizedAll;
+
 /*
  * ITrayWindow
  */
@@ -209,7 +283,7 @@ class CTrayWindow :
     HWND m_TaskSwitch;
     HWND m_TrayNotify;
 
-    CTrayNotifyWnd* m_TrayNotifyInstance;
+    CComPtr<IUnknown> m_TrayNotifyInstance;
 
     DWORD    m_Position;
     HMONITOR m_Monitor;
@@ -327,7 +401,19 @@ public:
 
     LRESULT DoExitWindows()
     {
+        /* Display the ReactOS Shutdown Dialog */
         ExitWindowsDialog(m_hWnd);
+
+        /*
+         * If the user presses CTRL+ALT+SHIFT while exiting
+         * the shutdown dialog, exit the shell cleanly.
+         */
+        if ((GetKeyState(VK_CONTROL) & 0x8000) &&
+            (GetKeyState(VK_SHIFT)   & 0x8000) &&
+            (GetKeyState(VK_MENU)    & 0x8000))
+        {
+            PostMessage(WM_QUIT, 0, 0);
+        }
         return 0;
     }
 
@@ -353,7 +439,13 @@ public:
 
         m_RunFileDlgOwner = hwnd;
 
-        RunFileDlg(hwnd, NULL, NULL, NULL, NULL, RFF_CALCDIRECTORY);
+        // build the default directory from two environment variables
+        CStringW strDefaultDir, strHomePath;
+        strDefaultDir.GetEnvironmentVariable(L"HOMEDRIVE");
+        strHomePath.GetEnvironmentVariable(L"HOMEPATH");
+        strDefaultDir += strHomePath;
+
+        RunFileDlg(hwnd, NULL, (LPCWSTR)strDefaultDir, NULL, NULL, RFF_CALCDIRECTORY);
 
         m_RunFileDlgOwner = NULL;
         ::DestroyWindow(hwnd);
@@ -467,6 +559,18 @@ public:
                      SW_SHOWNORMAL);
     }
 
+    VOID ToggleDesktop()
+    {
+        if (::IsThereAnyEffectiveWindow(TRUE))
+        {
+            ShowDesktop();
+        }
+        else
+        {
+            RestoreAll();
+        }
+    }
+
     BOOL STDMETHODCALLTYPE ExecContextMenuCmd(IN UINT uiCmd)
     {
         switch (uiCmd)
@@ -500,6 +604,7 @@ public:
             break;
 
         case ID_SHELL_CMD_SHOW_DESKTOP:
+            ShowDesktop();
             break;
 
         case ID_SHELL_CMD_TILE_WND_H:
@@ -523,6 +628,10 @@ public:
             ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
             break;
 
+        case ID_SHELL_CMD_RESTORE_ALL:
+            RestoreAll();
+            break;
+
         default:
             TRACE("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd);
             return FALSE;
@@ -544,7 +653,7 @@ public:
         case IDHK_EXPLORE:
             //FIXME: We don't support this yet:
             //ShellExecuteW(0, L"explore", NULL, NULL, NULL, 1);
-            ShellExecuteW(0, NULL, L"explorer.exe", NULL, NULL, 1); 
+            ShellExecuteW(0, NULL, L"explorer.exe", L"/e ,", NULL, 1);
             break;
         case IDHK_FIND:
             SHFindFiles(NULL, NULL);
@@ -561,10 +670,13 @@ public:
         case IDHK_PREV_TASK:
             break;
         case IDHK_MINIMIZE_ALL:
+            MinimizeAll();
             break;
         case IDHK_RESTORE_ALL:
+            RestoreAll();
             break;
         case IDHK_DESKTOP:
+            ToggleDesktop();
             break;
         case IDHK_PAGER:
             break;
@@ -577,35 +689,80 @@ public:
     {
         switch (uCommand)
         {
-        case IDM_TASKBARANDSTARTMENU:
-            DisplayProperties();
-            break;
-
-        case IDM_SEARCH:
-            SHFindFiles(NULL, NULL);
-            break;
-
-        case IDM_HELPANDSUPPORT:
-            ExecResourceCmd(IDS_HELP_COMMAND);
-            break;
-
-        case IDM_RUN:
-            DisplayRunFileDlg();
-            break;
-
-        /* FIXME: Handle these commands as well */
-        case IDM_SYNCHRONIZE:
-        case IDM_DISCONNECT:
-        case IDM_UNDOCKCOMPUTER:
-            break;
-
-        case IDM_LOGOFF:
-            LogoffWindowsDialog(m_hWnd); // FIXME: Maybe handle it in a similar way as DoExitWindows?
-            break;
+            case TRAYCMD_STARTMENU:
+                // TODO:
+                break;
+            case TRAYCMD_RUN_DIALOG:
+                DisplayRunFileDlg();
+                break;
+            case TRAYCMD_LOGOFF_DIALOG:
+                LogoffWindowsDialog(m_hWnd); // FIXME: Maybe handle it in a similar way as DoExitWindows?
+                break;
+            case TRAYCMD_CASCADE:
+                CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL);
+                break;
+            case TRAYCMD_TILE_H:
+                TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL);
+                break;
+            case TRAYCMD_TILE_V:
+                TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL);
+                break;
+            case TRAYCMD_TOGGLE_DESKTOP:
+                ToggleDesktop();
+                break;
+            case TRAYCMD_DATE_AND_TIME:
+                ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
+                break;
+            case TRAYCMD_TASKBAR_PROPERTIES:
+                DisplayProperties();
+                break;
+            case TRAYCMD_MINIMIZE_ALL:
+                MinimizeAll();
+                break;
+            case TRAYCMD_RESTORE_ALL:
+                RestoreAll();
+                break;
+            case TRAYCMD_SHOW_DESKTOP:
+                ShowDesktop();
+                break;
+            case TRAYCMD_SHOW_TASK_MGR:
+                OpenTaskManager(m_hWnd);
+                break;
+            case TRAYCMD_CUSTOMIZE_TASKBAR:
+                break;
+            case TRAYCMD_LOCK_TASKBAR:
+                if (SHRestricted(REST_CLASSICSHELL) == 0)
+                {
+                    Lock(!g_TaskbarSettings.bLock);
+                }
+                break;
+            case TRAYCMD_HELP_AND_SUPPORT:
+                ExecResourceCmd(IDS_HELP_COMMAND);
+                break;
+            case TRAYCMD_CONTROL_PANEL:
+                // TODO:
+                break;
+            case TRAYCMD_SHUTDOWN_DIALOG:
+                DoExitWindows();
+                break;
+            case TRAYCMD_PRINTERS_AND_FAXES:
+                // TODO:
+                break;
+            case TRAYCMD_LOCK_DESKTOP:
+                // TODO:
+                break;
+            case TRAYCMD_SWITCH_USER_DIALOG:
+                // TODO:
+                break;
+            case TRAYCMD_SEARCH_FILES:
+                SHFindFiles(NULL, NULL);
+                break;
+            case TRAYCMD_SEARCH_COMPUTERS:
+                SHFindComputer(NULL, NULL);
+                break;
 
-        case IDM_SHUTDOWN:
-            DoExitWindows();
-            break;
+            default:
+                break;
         }
 
         return FALSE;
@@ -690,8 +847,9 @@ public:
         IN BOOL TrackUp,
         IN PVOID Context OPTIONAL)
     {
-        INT x = ppt->x;
-        INT y = ppt->y;
+        POINT pt;
+        TPMPARAMS params;
+        RECT rc;
         HRESULT hr;
         UINT uCommand;
         HMENU popup = CreatePopupMenu();
@@ -699,6 +857,17 @@ public:
         if (popup == NULL)
             return E_FAIL;
 
+        if (ppt)
+        {
+            pt = *ppt;
+        }
+        else
+        {
+            ::GetWindowRect(m_hWnd, &rc);
+            pt.x = rc.left;
+            pt.y = rc.top;
+        }
+
         TRACE("Before Query\n");
         hr = contextMenu->QueryContextMenu(popup, 0, 0, UINT_MAX, CMF_NORMAL);
         if (FAILED_UNEXPECTEDLY(hr))
@@ -709,7 +878,20 @@ public:
         }
 
         TRACE("Before Tracking\n");
-        uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, x, y, m_hWnd, NULL);
+        ::SetForegroundWindow(m_hWnd);
+        if (hwndExclude)
+        {
+            ::GetWindowRect(hwndExclude, &rc);
+            ZeroMemory(&params, sizeof(params));
+            params.cbSize = sizeof(params);
+            params.rcExclude = rc;
+            uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, pt.x, pt.y, m_hWnd, &params);
+        }
+        else
+        {
+            uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, pt.x, pt.y, m_hWnd, NULL);
+        }
+        ::PostMessage(m_hWnd, WM_NULL, 0, 0);
 
         if (uCommand != 0)
         {
@@ -1311,8 +1493,8 @@ ChangePos:
         FitToRebar(&m_TrayRects[m_Position]);
 
         /* Move the tray window */
-        /* The handler of WM_WINDOWPOSCHANGING will override whatever size 
-           *and position we use here with m_TrayRects */
+        /* The handler of WM_WINDOWPOSCHANGING will override whatever size
+         and position we use here with m_TrayRects */
         SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE);
         ResizeWorkArea();
         ApplyClipping(TRUE);
@@ -2012,7 +2194,7 @@ ChangePos:
         if (!m_ContextMenu)
             return E_INVALIDARG;
 
-        return m_ContextMenu->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax);        
+        return m_ContextMenu->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax);
     }
 
 
@@ -2048,6 +2230,11 @@ ChangePos:
         if (FAILED_UNEXPECTEDLY(hRet))
             return FALSE;
 
+        /* Create the tray notification window */
+        hRet = CTrayNotifyWnd_CreateInstance(m_hWnd, IID_PPV_ARG(IUnknown, &m_TrayNotifyInstance));
+        if (FAILED_UNEXPECTEDLY(hRet))
+            return FALSE;
+
         /* Get the hwnd of the rebar */
         hRet = IUnknown_GetWindow(m_TrayBandSite, &m_Rebar);
         if (FAILED_UNEXPECTEDLY(hRet))
@@ -2058,10 +2245,12 @@ ChangePos:
         if (FAILED_UNEXPECTEDLY(hRet))
             return FALSE;
 
-        SetWindowTheme(m_Rebar, L"TaskBar", NULL);
+        /* Get the hwnd of the tray notification window */
+        hRet = IUnknown_GetWindow(m_TrayNotifyInstance, &m_TrayNotify);
+        if (FAILED_UNEXPECTEDLY(hRet))
+            return FALSE;
 
-        /* Create the tray notification window */
-        m_TrayNotify = CreateTrayNotifyWnd(this, &m_TrayNotifyInstance);
+        SetWindowTheme(m_Rebar, L"TaskBar", NULL);
 
         UpdateFonts();
 
@@ -2073,6 +2262,9 @@ ChangePos:
             SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
         }
 
+        /* Set the initial lock state in the band site */
+        m_TrayBandSite->Lock(g_TaskbarSettings.bLock);
+
         RegisterHotKey(m_hWnd, IDHK_RUN, MOD_WIN, 'R');
         RegisterHotKey(m_hWnd, IDHK_MINIMIZE_ALL, MOD_WIN, 'M');
         RegisterHotKey(m_hWnd, IDHK_RESTORE_ALL, MOD_WIN|MOD_SHIFT, 'M');
@@ -2150,10 +2342,7 @@ ChangePos:
     LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
     {
         if (m_TrayNotify)
-        {
-            TRACE("WM_COPYDATA notify message received. Handling...\n");
-            return TrayNotify_NotifyIconCmd(m_TrayNotifyInstance, wParam, lParam);
-        }
+            ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
         return TRUE;
     }
 
@@ -2164,6 +2353,10 @@ ChangePos:
             bHandled = FALSE;
             return 0;
         }
+        else if (g_TaskbarSettings.bLock)
+        {
+            return 0;
+        }
 
         return DrawSizerWithTheme((HRGN) wParam);
     }
@@ -2194,8 +2387,7 @@ ChangePos:
             pt.x = (SHORT) LOWORD(lParam);
             pt.y = (SHORT) HIWORD(lParam);
 
-            if (PtInRect(&rcClient,
-                pt))
+            if (PtInRect(&rcClient, pt))
             {
                 /* The user is trying to drag the tray window */
                 return HTCAPTION;
@@ -2225,7 +2417,6 @@ ChangePos:
             }
         }
         return HTBORDER;
-        return TRUE;
     }
 
     LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
@@ -2387,7 +2578,7 @@ ChangePos:
 
     LRESULT OnNcLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
     {
-        /* This handler implements the trick that makes  the start button to 
+        /* This handler implements the trick that makes  the start button to
            get pressed when the user clicked left or below the button */
 
         POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
@@ -2556,23 +2747,11 @@ HandleTrayContextMenu:
 
     LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
     {
+        /* Let the clock handle the double click */
+        ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
+
         /* We "handle" this message so users can't cause a weird maximize/restore
         window animation when double-clicking the tray window! */
-
-        /* We should forward mouse messages to child windows here.
-        Right now, this is only clock double-click */
-        RECT rcClock;
-        if (TrayNotify_GetClockRect(m_TrayNotifyInstance, &rcClock))
-        {
-            POINT ptClick;
-            ptClick.x = MAKEPOINTS(lParam).x;
-            ptClick.y = MAKEPOINTS(lParam).y;
-            if (PtInRect(&rcClock, ptClick))
-            {
-                //FIXME: use SHRunControlPanel
-                ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
-            }
-        }
         return TRUE;
     }
 
@@ -2627,6 +2806,88 @@ HandleTrayContextMenu:
         return HandleHotKey(wParam);
     }
 
+    struct MINIMIZE_INFO
+    {
+        HWND hwndDesktop;
+        HWND hTrayWnd;
+        HWND hwndProgman;
+        BOOL bRet;
+        CSimpleArray<HWND> *pMinimizedAll;
+        BOOL bShowDesktop;
+    };
+
+    static BOOL IsDialog(HWND hwnd)
+    {
+        WCHAR szClass[32];
+        GetClassNameW(hwnd, szClass, _countof(szClass));
+        return wcscmp(szClass, L"#32770") == 0;
+    }
+
+    static BOOL CALLBACK MinimizeWindowsProc(HWND hwnd, LPARAM lParam)
+    {
+        MINIMIZE_INFO *info = (MINIMIZE_INFO *)lParam;
+        if (hwnd == info->hwndDesktop || hwnd == info->hTrayWnd ||
+            hwnd == info->hwndProgman)
+        {
+            return TRUE;
+        }
+        if (!info->bShowDesktop)
+        {
+            if (!::IsWindowEnabled(hwnd) || IsDialog(hwnd))
+                return TRUE;
+            HWND hwndOwner = ::GetWindow(hwnd, GW_OWNER);
+            if (hwndOwner && !::IsWindowEnabled(hwndOwner))
+                return TRUE;
+        }
+        if (::IsWindowVisible(hwnd) && !::IsIconic(hwnd))
+        {
+            ::ShowWindowAsync(hwnd, SW_MINIMIZE);
+            info->bRet = TRUE;
+            info->pMinimizedAll->Add(hwnd);
+        }
+        return TRUE;
+    }
+
+    VOID MinimizeAll(BOOL bShowDesktop = FALSE)
+    {
+        MINIMIZE_INFO info;
+        info.hwndDesktop = GetDesktopWindow();;
+        info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
+        info.hwndProgman = FindWindowW(L"Progman", NULL);
+        info.bRet = FALSE;
+        info.pMinimizedAll = &g_MinimizedAll;
+        info.bShowDesktop = bShowDesktop;
+        EnumWindows(MinimizeWindowsProc, (LPARAM)&info);
+
+        // invalid handles should be cleared to avoid mismatch of handles
+        for (INT i = 0; i < g_MinimizedAll.GetSize(); ++i)
+        {
+            if (!::IsWindow(g_MinimizedAll[i]))
+                g_MinimizedAll[i] = NULL;
+        }
+
+        ::SetForegroundWindow(m_DesktopWnd);
+        ::SetFocus(m_DesktopWnd);
+    }
+
+    VOID ShowDesktop()
+    {
+        MinimizeAll(TRUE);
+    }
+
+    VOID RestoreAll()
+    {
+        for (INT i = g_MinimizedAll.GetSize() - 1; i >= 0; --i)
+        {
+            HWND hwnd = g_MinimizedAll[i];
+            if (::IsWindowVisible(hwnd) && ::IsIconic(hwnd))
+            {
+                ::ShowWindow(hwnd, SW_RESTORE);
+            }
+        }
+        g_MinimizedAll.RemoveAll();
+    }
+
     LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
     {
         LRESULT Ret = FALSE;
@@ -2697,6 +2958,24 @@ HandleTrayContextMenu:
         return 0;
     }
 
+    LRESULT OnInitMenuPopup(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
+    {
+        HMENU hMenu = (HMENU)wParam;
+        if (::IsThereAnyEffectiveWindow(FALSE))
+        {
+            ::EnableMenuItem(hMenu, ID_SHELL_CMD_CASCADE_WND, MF_BYCOMMAND | MF_ENABLED);
+            ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_H, MF_BYCOMMAND | MF_ENABLED);
+            ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_V, MF_BYCOMMAND | MF_ENABLED);
+        }
+        else
+        {
+            ::EnableMenuItem(hMenu, ID_SHELL_CMD_CASCADE_WND, MF_BYCOMMAND | MF_GRAYED);
+            ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_H, MF_BYCOMMAND | MF_GRAYED);
+            ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_V, MF_BYCOMMAND | MF_GRAYED);
+        }
+        return 0;
+    }
+
     LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
     {
 #if 0
@@ -2708,14 +2987,14 @@ HandleTrayContextMenu:
         RECT rc;
         ::GetWindowRect(m_hWnd, &rc);
 
-        SIZE szWindow = { 
-            rc.right - rc.left, 
+        SIZE szWindow = {
+            rc.right - rc.left,
             rc.bottom - rc.top };
-        SIZE szTarget = { 
-            as->rcTarget.right - as->rcTarget.left, 
+        SIZE szTarget = {
+            as->rcTarget.right - as->rcTarget.left,
             as->rcTarget.bottom - as->rcTarget.top };
-        SIZE szActual = { 
-            as->rcActual.right - as->rcActual.left, 
+        SIZE szActual = {
+            as->rcActual.right - as->rcActual.left,
             as->rcActual.bottom - as->rcActual.top };
 
         SIZE borders = {
@@ -2833,12 +3112,13 @@ HandleTrayContextMenu:
         MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
         MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
         MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
-        MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
-        MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
         MESSAGE_HANDLER(WM_CLOSE, OnDoExitWindows)
         MESSAGE_HANDLER(WM_HOTKEY, OnHotkey)
         MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize)
+        MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup)
         MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
+        MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
+        MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
     ALT_MSG_MAP(1)
     END_MSG_MAP()
 
@@ -2979,15 +3259,29 @@ public:
         return S_OK;
     }
 
-    virtual HRESULT STDMETHODCALLTYPE 
+    virtual HRESULT STDMETHODCALLTYPE
         QueryContextMenu(HMENU hPopup,
                          UINT indexMenu,
                          UINT idCmdFirst,
                          UINT idCmdLast,
                          UINT uFlags)
     {
-        HMENU menubase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCEW(IDM_TRAYWND));
-        if (!menubase)
+        HMENU hMenuBase;
+
+        hMenuBase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCEW(IDM_TRAYWND));
+
+        if (g_MinimizedAll.GetSize() != 0 && !::IsThereAnyEffectiveWindow(TRUE))
+        {
+            CStringW strRestoreAll(MAKEINTRESOURCEW(IDS_RESTORE_ALL));
+            MENUITEMINFOW mii = { sizeof(mii) };
+            mii.fMask = MIIM_ID | MIIM_TYPE;
+            mii.wID = ID_SHELL_CMD_RESTORE_ALL;
+            mii.fType = MFT_STRING;
+            mii.dwTypeData = const_cast<LPWSTR>(&strRestoreAll[0]);
+            SetMenuItemInfoW(hMenuBase, ID_SHELL_CMD_SHOW_DESKTOP, FALSE, &mii);
+        }
+
+        if (!hMenuBase)
             return HRESULT_FROM_WIN32(GetLastError());
 
         if (SHRestricted(REST_CLASSICSHELL) != 0)
@@ -2997,15 +3291,15 @@ public:
                        MF_BYCOMMAND);
         }
 
-        CheckMenuItem(hPopup,
+        CheckMenuItem(hMenuBase,
                       ID_LOCKTASKBAR,
                       MF_BYCOMMAND | (g_TaskbarSettings.bLock ? MF_CHECKED : MF_UNCHECKED));
 
         UINT idCmdNext;
-        idCmdNext = Shell_MergeMenus(hPopup, menubase, indexMenu, idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS | MM_ADDSEPARATOR);
+        idCmdNext = Shell_MergeMenus(hPopup, hMenuBase, indexMenu, idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS | MM_ADDSEPARATOR);
         m_idCmdCmFirst = idCmdNext - idCmdFirst;
 
-        ::DestroyMenu(menubase);
+        ::DestroyMenu(hMenuBase);
 
         if (TrayWnd->m_TrayBandSite != NULL)
         {