[EXPLORER][SHELL32][USER32] Implement 'Show the Desktop' action of Task Bar (#668)
[reactos.git] / base / shell / explorer / traywnd.cpp
index aec731d..8fd6c55 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "precomp.h"
 #include <commoncontrols.h>
+#include <traycmd.h>
 
 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu);
 
@@ -56,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
  */
@@ -486,6 +559,18 @@ public:
                      SW_SHOWNORMAL);
     }
 
+    VOID ToggleDesktop()
+    {
+        if (::IsThereAnyEffectiveWindow(TRUE))
+        {
+            ShowDesktop();
+        }
+        else
+        {
+            RestoreAll();
+        }
+    }
+
     BOOL STDMETHODCALLTYPE ExecContextMenuCmd(IN UINT uiCmd)
     {
         switch (uiCmd)
@@ -519,6 +604,7 @@ public:
             break;
 
         case ID_SHELL_CMD_SHOW_DESKTOP:
+            ShowDesktop();
             break;
 
         case ID_SHELL_CMD_TILE_WND_H:
@@ -542,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;
@@ -580,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;
@@ -596,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;
@@ -2668,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;
@@ -2738,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
@@ -2877,6 +3115,7 @@ HandleTrayContextMenu:
         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)
@@ -3027,8 +3266,22 @@ public:
                          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)
@@ -3038,15 +3291,15 @@ public:
                        MF_BYCOMMAND);
         }
 
-        CheckMenuItem(menubase,
+        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)
         {