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 "FolderItem.h" 
    32: #include "Menu.h" 
    33: #include "MenuCommon.h" 
    34: #include "MenuItem.h" 
    35: #include "..\Settings\Settings.h" 
    36: #include "..\Sounds\Sounds.h" 
    37: 
    38: extern MenuCommon *pMenuCommon;
    39: extern Settings *pSettings;
    40: 
    41: //===========================================================================
    42: 
    43: FolderItem::FolderItem(Menu* pSubMenu, char* pszTitleANSI, wchar_t* pszTitleUnicode) : MenuItem(MENUITEM_FOLDER, pszTitleANSI, pszTitleUnicode, NULL, NULL, false)
    44: {
    45:     m_pSubMenu = pSubMenu;
    46:     m_nTimerId = 0;
    47:     SetSortPriority(PRIORITY_FOLDER);
    48: }
    49: 
    50: FolderItem::~FolderItem()
    51: {
    52:     m_pParent->RemoveChild(m_pSubMenu);
    53:     if (m_pSubMenu) delete m_pSubMenu;
    54:     m_pSubMenu = NULL;
    55: }
    56: 
    57: //===========================================================================
    58: 
    59: void FolderItem::Attached(Menu* pMenu)
    60: {
    61:     MenuItem::Attached(pMenu);  
    62: 
    63:     m_pParent->AddChild(m_pSubMenu);
    64:     m_pSubMenu->SetParent(m_pParent);
    65: }
    66: 
    67: //====================
    68: 
    69: void FolderItem::Invoke()
    70: {
    71:     Active(true);
    72: }
    73: 
    74: //====================
    75: 
    76: bool FolderItem::Active(bool bActivate)
    77: {
    78:     bool result = MenuItem::Active(bActivate);
    79: 
    80:     if (bActivate)
    81:     {
    82:         if (!IsWindowVisible(m_pSubMenu->GetWindow()) && m_nTimerId == 0)
    83:         {
    84:             m_nTimerId = (UINT)this;
    85:             SetTimer(GetWindow(), m_nTimerId, pSettings->submenuDelay, NULL);
    86:         }
    87:     }
    88:     else
    89:     {   
    90:         if (m_nTimerId != 0) KillTimer(GetWindow(), m_nTimerId);
    91:         m_nTimerId = 0;
    92: 
    93:         if (m_pSubMenu != NULL)
    94:         {
    95:             if (IsWindowVisible(m_pSubMenu->GetWindow())) m_pSubMenu->Hide(HIDE_CHILDREN);
    96:         }
    97:     }
    98: 
    99:     return result;
   100: }
   101: 
   102: //====================
   103: 
   104: // Handles timer messages... (folders use a
   105: // timer to wait before submenus are opened)
   106: 
   107: void FolderItem::Timer(int nTimer)
   108: {
   109:     if ((UINT)nTimer == m_nTimerId)
   110:     {
   111:         ShowSubMenu();
   112:         KillTimer(GetWindow(), m_nTimerId);
   113:         m_nTimerId = 0;
   114:     }
   115: }
   116: 
   117: //====================
   118: 
   119: void FolderItem::ShowSubMenu()
   120: {
   121:     if (m_pSubMenu->IsPinned()) return;
   122: 
   123:     //====================
   124: 
   125:     RECT r;
   126:     RECT s;
   127:     RECT screen;
   128:     RECT rParent;
   129:     Menu* pParent;
   130:     int subMenuX, subMenuY;
   131:     int subMenuWidth, subMenuHeight;
   132: 
   133:     //====================
   134: 
   135:     // Current menu...
   136:     GetWindowRect(GetWindow(), &r);
   137: 
   138:     // Get the dimensions of the monitor where the current menu is located...
   139:     POINT pt = {r.left, r.top};
   140:     HMONITOR hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
   141: 
   142:     MONITORINFO mi;
   143:     mi.cbSize = sizeof(mi);
   144: 
   145:     if (GetMonitorInfo(hMon, &mi))
   146:     {
   147:         screen.left = mi.rcMonitor.left;
   148:         screen.top = mi.rcMonitor.top;
   149:         screen.right = mi.rcMonitor.right;
   150:         screen.bottom = mi.rcMonitor.bottom;
   151:     }
   152:     else
   153:     {
   154:         screen.left = 0;
   155:         screen.top = 0;
   156:         screen.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
   157:         screen.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
   158:     }
   159: 
   160:     //====================
   161: 
   162:     // Submenu...
   163:     GetWindowRect(m_pSubMenu->GetWindow(), &s);
   164:     subMenuWidth = s.right - s.left;
   165:     subMenuHeight = s.bottom - s.top;
   166: 
   167:     // Parent menu...
   168:     pParent = m_pParent->GetParent();
   169:     if (pParent) GetWindowRect(pParent->GetWindow(), &rParent);
   170:     else
   171:     {
   172:         // Fallbacks if the current menu has no parent...
   173:         rParent.left = screen.left;
   174:         rParent.top = screen.top;
   175:     }
   176: 
   177:     //====================
   178: 
   179:     // By default we try opening submenus in the same direction
   180:     // as the current menu was opened (if it too has a parent)...
   181:     if (rParent.left < r.left)
   182:     {
   183:         // Parent menu to the left of the current menu or no parent menu
   184:         // -> default submenu position to the right of the current menu
   185: //      subMenuX = r.right - pSettings->MenuFrame->borderWidth - pSettings->MenuFrame->marginWidth;
   186:         subMenuX = r.right - pSettings->MenuFrame->borderWidth;
   187:         if (pSettings->menuRoundedCorners || !pSettings->MenuFrameBorder->parentRelative) subMenuX += pSettings->submenuGap;
   188: 
   189:         // If the submenu is partly or fully off screen we
   190:         // move it to the left of the current menu instead...
   191:         if ((subMenuX + subMenuWidth) > screen.right)
   192:         {
   193: //          subMenuX = r.left - subMenuWidth + pSettings->menuBorderWidth + pSettings->MenuFrame->marginWidth;
   194:             subMenuX = r.left - subMenuWidth + pSettings->MenuFrame->borderWidth;
   195:             if (pSettings->menuRoundedCorners || !pSettings->MenuFrameBorder->parentRelative) subMenuX -= pSettings->submenuGap;
   196:         }
   197:     }
   198:     else
   199:     {
   200:         // Parent menu to the right of the current menu
   201:         // -> default submenu position to the left of the current menu
   202: //      subMenuX = r.left - subMenuWidth + pSettings->menuBorderWidth + pSettings->MenuFrame->marginWidth;
   203:         subMenuX = r.left - subMenuWidth + pSettings->MenuFrame->borderWidth;
   204:         if (pSettings->menuRoundedCorners || !pSettings->MenuFrameBorder->parentRelative) subMenuX -= pSettings->submenuGap;
   205: 
   206:         // If the submenu is partly or fully off screen we
   207:         // move it to the right of the current menu instead...
   208:         if (subMenuX < screen.left)
   209:         {
   210:             subMenuX = r.right - pSettings->MenuFrame->borderWidth;
   211:             if (pSettings->menuRoundedCorners || !pSettings->MenuFrameBorder->parentRelative) subMenuX += pSettings->submenuGap;
   212:         }
   213:     }
   214: 
   215:     // Calculating the y position is slightly easier... :)
   216: //  subMenuY = r.top + m_nTop - pMenuCommon->m_nTitleHeight - pSettings->MenuFrame->marginWidth - pSettings->MenuFrame->borderWidth;
   217:     subMenuY = r.top + m_nTop - pMenuCommon->m_nTitleHeight - pSettings->MenuFrame->marginWidth;
   218:     // Check if *only* the legacy menu.borderWidth was defined...
   219:     if ((pSettings->menuBorderWidth > 0) && (pSettings->menuBorderWidth == pSettings->MenuFrame->borderWidth) && (pSettings->menuBorderWidth == pSettings->MenuTitle->borderWidth))
   220:     {
   221:         // ...and if so do nothing... (I think ;))
   222:     }
   223:     // ...otherwise adjust the y position as applicable for modern syntax rendering... (including support for the bbLean menu title/frame borders bug/feature, see Menu.cpp)
   224:     else if (!pMenuCommon->m_nTitleDisabled && (pSettings->MenuFrame->borderWidth >= pSettings->MenuTitle->borderWidth)) subMenuY -= pSettings->MenuFrame->borderWidth;
   225: 
   226:     if (subMenuY < screen.top) subMenuY = screen.top;
   227:     if ((subMenuY + subMenuHeight) > screen.bottom) subMenuY = screen.bottom - subMenuHeight;
   228: 
   229:     //====================
   230: 
   231:     // Move focus to the newly shown submenu.. 
   232:     SetForegroundWindow(GetWindow()); 
   233: 
   234:     m_pSubMenu->Show(subMenuX, subMenuY);
   235:     SendMessage(GetBBWnd(), BB_SUBMENU, 0, 0);
   236:     PlaySoundFX(SFX_MENU_SUBFOLDER);
   237: }
   238: 
   239: //===========================================================================
   240: 
   241: // ====================================================================
   242: // Below: SpecialFolder is an extension to the FolderItem that supports
   243: // dynamic reloading of the folder file structure when activated...
   244: // ====================================================================
   245: 
   246: SpecialFolder::SpecialFolder(char* pszTitleANSI, wchar_t* pszTitleUnicode, char* pszPath, int menuType) : FolderItem(NULL, pszTitleANSI, pszTitleUnicode)
   247: {
   248:     m_pszPath = pszPath ? _strdup(pszPath) : _strdup("");
   249:     m_MenuType = menuType;
   250: }
   251: 
   252: SpecialFolder::~SpecialFolder()
   253: {
   254:     if (m_pszPath) free(m_pszPath);
   255:     m_pszPath = NULL;
   256: 
   257:     if (m_pSubMenu)
   258:     {
   259:         m_pParent->RemoveChild(m_pSubMenu);
   260:         delete m_pSubMenu;
   261:     }
   262:     m_pSubMenu = NULL;
   263: }
   264: 
   265: //===========================================================================
   266: 
   267: void SpecialFolder::Invoke(int button)
   268: {
   269:     char folder[MAX_LINE_LENGTH], arg[MAX_LINE_LENGTH];
   270:     strcpy_s(folder, sizeofArray(folder), m_pszPath);
   271: 
   272:     if (button == 2)
   273:     {
   274:         //      m_pParent->Hide(HIDE_PARENTS);
   275:         pMenuCommon->HideAfterInvoke(this, button);
   276:         MenuItem::Invoke(button);
   277: 
   278:         if (strchr(folder, '|'))
   279:         {
   280:             // If this is a merged [path] item we use
   281:             // the first path when right clicking on it...
   282:             Tokenize(folder, arg, "|");
   283:             strcpy_s(folder, sizeofArray(folder), arg); // Note: Just using arg as a temp variable here...
   284:         }
   285: 
   286:         if (folder[0] == '\"') StrRemoveEncap(folder);
   287:         strcpy_s(arg, sizeofArray(arg), "/e,");
   288:         strcat_s(arg, sizeofArray(arg), folder);
   289: 
   290:         BBExecute(GetDesktopWindow(), NULL, "explorer.exe", arg, folder, SW_SHOWNORMAL, false);
   291:     }
   292:     else if (button == 3)
   293:     {
   294:         if (folder[0] == '\"') StrRemoveEncap(folder);
   295:         CopyStringToClipboard(folder);
   296:     }
   297: }
   298: 
   299: //====================
   300: 
   301: bool SpecialFolder::Active(bool bActivate)
   302: {
   303:     if (m_bActive == bActivate && m_pSubMenu)
   304:     {
   305:         if (IsWindowVisible(m_pSubMenu->GetWindow())) return bActivate;
   306:     }
   307: 
   308:     if (bActivate)
   309:     {
   310:         if (m_pSubMenu)
   311:         {
   312:             if (m_pSubMenu->IsPinned()) return FolderItem::Active(bActivate);
   313:             ShowWindow(m_pSubMenu->GetWindow(), SW_HIDE);
   314:             m_pParent->RemoveChild(m_pSubMenu);
   315:             delete m_pSubMenu;
   316:         }
   317: 
   318:         m_pSubMenu = new Menu(pMenuCommon->hInst);
   319: 
   320:         pMenuCommon->AddHeaderItem(m_pSubMenu, m_pszTitleANSI);
   321:         pMenuCommon->InsertFolder(m_pSubMenu, m_pszPath, m_MenuType, true);
   322:         pMenuCommon->AddFooterItem(m_pSubMenu, "");
   323: 
   324:         m_pSubMenu->Invalidate();
   325:         m_pSubMenu->Validate();
   326: 
   327:         m_pParent->AddChild(m_pSubMenu);
   328:         m_pSubMenu->SetParent(m_pParent);
   329:         m_pSubMenu->SetMenuFolderPath(m_pszPath);
   330:     }
   331: 
   332:     return FolderItem::Active(bActivate);
   333: }
   334: 
   335: //====================
   336: 
   337: void SpecialFolder::Attached(Menu* pMenu)
   338: {
   339:     m_pParent = pMenu;
   340: }
   341: 
   342: //===========================================================================
   343: