1: /*
     2:  ============================================================================
     3:  xoblite™ -> an advanced "extended shell" for Microsoft® Windows® 11
     4:  Copyright © 2002-2026 Karl Henrik Henriksson [qwilk/@xoblite]
     5:  Copyright © 2001-2004 The Blackbox for Windows Development Team
     6:  http://xoblite.net/ + https://github.com/xoblite/
     7:  ============================================================================
     8: 
     9:   Blackbox for Windows is free software, released under the
    10:   GNU General Public License (GPL version 2 or later), with an extension
    11:   that allows linking of proprietary modules under a controlled interface.
    12:   What this means is that plugins etc. are allowed to be released
    13:   under any license the author wishes. Please note, however, that the
    14:   original Blackbox gradient math code used in Blackbox for Windows
    15:   is available under the BSD license.
    16: 
    17:   http://www.fsf.org/licenses/gpl.html
    18:   http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
    19:   http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
    20: 
    21:   This program is distributed in the hope that it will be useful,
    22:   but WITHOUT ANY WARRANTY; without even the implied warranty of
    23:   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    24:   GNU General Public License for more details.
    25: 
    26:   For additional license information, please read the included license.html.
    27: 
    28:  ============================================================================
    29: */
    30: 
    31: #include "WorkspacesMenu.h" 
    32: #include "..\API\BBApi.h" 
    33: #include "..\Menu\MenuCommon.h" 
    34: #include "..\Menu\MenuItem.h" 
    35: #include "..\Settings\Settings.h" 
    36: #include "..\Workspaces\Workspaces.h" 
    37: 
    38: #include <dwmapi.h> 
    39: 
    40: extern MenuCommon *pMenuCommon;
    41: extern Settings *pSettings;
    42: extern Workspaces *pWorkspaces;
    43: 
    44: #define WORKSPACES_FOLDER_TIMER 4434 
    45: 
    46: //===========================================================================
    47: 
    48: WorkspacesMenu::WorkspacesMenu(const char *pszTitle) : Menu(pMenuCommon->hInst)
    49: {
    50:     m_pszTitle = pszTitle ? _strdup(pszTitle) : _strdup("");
    51: }
    52: 
    53: WorkspacesMenu::~WorkspacesMenu()
    54: {
    55:     if (m_pszTitle) free(m_pszTitle);
    56:     m_pszTitle = NULL;
    57: }
    58: 
    59: //===========================================================================
    60: 
    61: void WorkspacesMenu::OnShow(bool fShow)
    62: {
    63:     if (fShow) UpdateFolder();
    64: }
    65: 
    66: //====================
    67: 
    68: bool WorkspacesMenu::OnUser(int nMessage, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
    69: {
    70:     if (nMessage == BB_DESKTOPINFO)
    71:     {
    72:         DesktopInfo *pDesktopInfo = (DesktopInfo*)lParam;
    73: 
    74:         char desktopNameANSI[MAX_LINE_LENGTH];
    75:         strcpy_s(desktopNameANSI, sizeofArray(desktopNameANSI), pDesktopInfo->name);
    76:         strcat_s(desktopNameANSI, sizeofArray(desktopNameANSI), "\0"); // Just in case...
    77:         wchar_t desktopNameUnicode[MAX_LINE_LENGTH];
    78:         MultiByteToWideChar(CP_UTF8, 0, desktopNameANSI, strnlen_s(desktopNameANSI, sizeofArray(desktopNameANSI))+1, desktopNameUnicode, MAX_LINE_LENGTH);
    79: 
    80:         WorkspacesMenuItem *item = new WorkspacesMenuItem(desktopNameANSI, desktopNameUnicode, pDesktopInfo->number);
    81:         item->SetHeight(pMenuCommon->m_nSubmenuHeight);
    82:         AddMenuItem(item);
    83: 
    84:         return true;
    85:     }
    86: 
    87:     return Menu::OnUser(nMessage, wParam, lParam, lResult);
    88: }
    89: 
    90: //====================
    91: 
    92: void WorkspacesMenu::UpdateFolder()
    93: {
    94:     DeleteMenuItems();
    95: 
    96:     pMenuCommon->AddHeaderItem(this, m_pszTitle);
    97: 
    98:     WorkspacesMenuItem *item = new WorkspacesMenuItem("Minimized windows", L"Minimized windows", 255);
    99:     item->SetHeight(pMenuCommon->m_nSubmenuHeight);
   100:     AddMenuItem(item);
   101: 
   102:     pMenuCommon->CreateMenuItem(this, MENUITEM_SEPARATOR, NULL, NULL, NULL, false);
   103: 
   104:     SendMessage(GetBBWnd(), BB_LISTDESKTOPS, (WPARAM)GetWindow(), 0);
   105: 
   106:     pMenuCommon->CreateMenuItem(this, MENUITEM_SEPARATOR, NULL, NULL, NULL, false);
   107: 
   108:     pMenuCommon->CreateMenuItem(this, MENUITEM_COMMAND, "@xoblite RestoreAll", NULL, "Restore all windows...", false);
   109:     pMenuCommon->CreateMenuItem(this, MENUITEM_COMMAND, "@xoblite MinimizeAll", NULL, "Minimize all windows...", false);
   110: 
   111:     pMenuCommon->CreateMenuItem(this, MENUITEM_SEPARATOR, NULL, NULL, NULL, false);
   112: 
   113:     pMenuCommon->CreateMenuItem(this, MENUITEM_COMMAND, "@xoblite Workspaces", "GatherWindows", "Gather all windows...", false);
   114: 
   115:     pMenuCommon->CreateMenuItem(this, MENUITEM_SEPARATOR, NULL, NULL, NULL, false);
   116: 
   117:     pMenuCommon->CreateMenuItem(this, MENUITEM_COMMAND, "@xoblite Workspaces", "New", "New workspace", false);
   118:     pMenuCommon->CreateMenuItem(this, MENUITEM_COMMAND, "@xoblite Workspaces", "Remove", "Remove last workspace", false);
   119: 
   120:     pMenuCommon->CreateMenuItem(this, MENUITEM_SEPARATOR, NULL, NULL, NULL, false);
   121: 
   122:     pMenuCommon->CreateMenuItem(this, MENUITEM_BOOLEAN, "@xoblite Workspaces", "ToggleMousewheelChanging", "Mousewheel changing", pSettings->mousewheelChanging);
   123:     pMenuCommon->CreateMenuItem(this, MENUITEM_BOOLEAN, "@xoblite Workspaces", "ToggleFollowActive", "Follow active task", pSettings->followActive);
   124:     pMenuCommon->CreateMenuItem(this, MENUITEM_BOOLEAN, "@xoblite Workspaces", "ToggleWallpaperPerWorkspace", "Wallpaper per workspace", pSettings->wallpaperPerWorkspace);
   125: 
   126:     pMenuCommon->AddFooterItem(this, "");
   127: 
   128:     Invalidate();
   129:     Validate();
   130: }
   131: 
   132: //===========================================================================
   133: 
   134: WorkspacesMenuItem::WorkspacesMenuItem(char* pszTitleANSI, wchar_t* pszTitleUnicode, int nDesktop) : FolderItem(NULL, pszTitleANSI, pszTitleUnicode)
   135: {
   136:     m_nDesktop = nDesktop;
   137:     m_nTimer = 0;
   138: }
   139: 
   140: WorkspacesMenuItem::~WorkspacesMenuItem()
   141: {
   142:     m_pParent->RemoveChild(m_pSubMenu);
   143:     if (m_pSubMenu) delete m_pSubMenu;
   144:     m_pSubMenu = NULL;
   145: }
   146: 
   147: //===========================================================================
   148: 
   149: void WorkspacesMenuItem::Invoke()
   150: {
   151:     m_pParent->Hide(HIDE_CHILDREN);
   152:     m_pParent->Hide(HIDE_PARENTS);
   153:     pWorkspaces->SwitchToWorkspace(m_nDesktop);
   154: }
   155: 
   156: //====================
   157: 
   158: bool WorkspacesMenuItem::Active(bool bActivate)
   159: {
   160:     if (bActivate && !m_bActive) UpdateFolder();
   161:     return FolderItem::Active(bActivate);
   162: }
   163: 
   164: //====================
   165: 
   166: void WorkspacesMenuItem::UpdateFolder()
   167: {
   168:     m_pSubMenu->DeleteMenuItems();
   169: 
   170:     pMenuCommon->AddHeaderItem(m_pSubMenu, m_pszTitleANSI);
   171: 
   172:     numberOfTasksOnThisWorkspace = 0;
   173:     EnumWindows(CheckTaskEnumProc, (LPARAM)this);
   174:     if (numberOfTasksOnThisWorkspace == 0) pMenuCommon->CreateMenuItem(m_pSubMenu, MENUITEM_NOP, NULL, NULL, "(No tasks on this workspace)", false);
   175: 
   176:     pMenuCommon->AddFooterItem(m_pSubMenu, "");
   177: 
   178:     m_pSubMenu->Sort(2,2); // Skip the header+footer items and top+bottom marginWidth padding items...
   179: 
   180:     m_pSubMenu->Invalidate();
   181:     m_pSubMenu->Validate();
   182: }
   183: 
   184: //====================
   185: 
   186: void WorkspacesMenuItem::AddWindow(HWND hWnd)
   187: {
   188:     char pszWindowText[1024], wndstring[10];
   189:     wchar_t pszWindowTextUnicode[1024];
   190: 
   191:     // First we get the window title text...
   192:     GetWindowText(hWnd, pszWindowText, sizeof(pszWindowText));
   193:     GetWindowTextW(hWnd, pszWindowTextUnicode, sizeof(pszWindowTextUnicode));
   194:     // ...then we convert its hwnd to a string with hexadecimal format... (pretty cool, eh? :D )
   195:     _ultoa((unsigned long)hWnd, wndstring, 16);
   196:     // Finally, we create a normal CommandItem but with the hwnd-string as argument!
   197:     pMenuCommon->CreateMenuItemUnicode(m_pSubMenu, MENUITEM_COMMAND, "<task_item>", wndstring, pszWindowTextUnicode, false);
   198: 
   199:     numberOfTasksOnThisWorkspace++;
   200: }
   201: 
   202: void WorkspacesMenuItem::Attached(Menu* pMenu)
   203: {
   204:     m_pParent = pMenu;
   205:     m_pSubMenu = new Menu(pMenuCommon->hInst);
   206:     m_pParent->AddChild(m_pSubMenu);
   207:     m_pSubMenu->SetParent(m_pParent);
   208:     SetTimer(GetWindow(), WORKSPACES_FOLDER_TIMER, 2000, NULL);
   209: }
   210: 
   211: void WorkspacesMenuItem::Timer(int nTimer)
   212: {
   213:     FolderItem::Timer(nTimer);
   214: 
   215:     if (nTimer == WORKSPACES_FOLDER_TIMER && IsWindowVisible(m_pSubMenu->GetWindow()))
   216:     {
   217:         UpdateFolder();
   218:     }
   219: }
   220: 
   221: //===========================================================================
   222: 
   223: BOOL CALLBACK CheckTaskEnumProc(HWND hwnd, LPARAM lParam)
   224: {
   225:     if (!IsAppWindow(hwnd)) return true;
   226: 
   227:     // Check for DWM cloaked windows... (e.g. preloading Windows 10 apps like Microsoft Edge etc, see also Taskbar::CheckCloakedWindow())
   228:     int cloaked = 0;
   229:     HRESULT hr = DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, 4);
   230:     if ((hr == S_OK) && (cloaked > 0)) return true;
   231: 
   232:     //====================
   233: 
   234:     WorkspacesMenuItem* wmi = (WorkspacesMenuItem*)lParam;
   235: 
   236:     if (wmi->m_nDesktop == 255) // -> Minimized tasks
   237:     {
   238:         if (IsIconic(hwnd)) wmi->AddWindow(hwnd);
   239:     }
   240:     else if (wmi->m_nDesktop == 254) // -> All tasks
   241:     {
   242:         wmi->AddWindow(hwnd);
   243:     }
   244:     else // -> Tasks on this specific workspace?
   245:     {
   246:         if (!IsIconic(hwnd) && (pWorkspaces->GetWorkspaceByWindow(hwnd) == wmi->m_nDesktop)) wmi->AddWindow(hwnd);
   247:     }
   248: 
   249:     return true;
   250: }
   251: 
   252: //===========================================================================
   253: